题目描述
小A最近喜欢上一款游戏:游戏把地图分了一些区域,这些区域可能会重叠,也可能不会。
游戏中有一项传送技能,改传送技能只能将在同一区域的两个地方使用。小A可以利用区域中重叠部分来实现从某一区域到另一个区域的移动。
当然技能会消耗魔法值,对于同一区域内两点,消耗的魔法值是相同的。如下图:
有红蓝两个区域,共4个地点1 2 3 4,假设在红色区域移动只需消耗a1点魔法值,在蓝色区域间移动需花费a2点魔法值,那么1和2之间或者1和3之间都需花费a1点魔法,2和4或者3和4之间都需要a2点魔法,2和3之间选择消耗a1点魔法也可以选择消耗a2点魔法。
现在小A在S点接到了一个任务,需要到达F点去做任务,再到P点交任务。
现在让你求一下完成该任务,需要因为传送消耗的魔法值至少为多少。
当然有些地方需要花RMB才能到达,即你从S点无论如何也无法到达F点去做任务,或者F点无法到达P点。因为双11,小A已经没有RMB了,这是你只需告诉他“Poor guy, you don’t have enough Money!”。
输入
多组样例输入(不会超过10组)
对于每一组样例:
第一行有五个整数 n m S F P 表示有n个地点和m个区域(2<=n<=100000) (0<m<1000000)。S F P为题目中所述的地点编号。(地点编号为:1,2,3......,n).
接下来每行描述的是每一个区域的信息,
ai bi 表示 在第i块区域内需要消耗ai点魔法值,第i各区域有bi个地点,然后有bi个数,代表第i个区域内第地点编号。(1<=ai<=1000000000, bi>0) .
保证所有bi的和小于1000000.
输出
如果无解,则输出“Poor guy, you don’t have enough Money!”(引号中的内容)。
否则就输出最少的消耗的魔法量。
样例输入
4 2 1 4 2 3 3 1 2 3 5 3 2 3 4
样例输出
13
思路:把区域当做节点,穿过某个区域抽象成路过某个节点,进入该节点话费为ai,出节点免费,链接点和区域,然后以目标点(中间点)为原点做一遍单源最短路,我用的是Dijkstra,最后时间为7000+ms,空间为50000kb+,空间还好,是提交的里面几乎最小的,但是时间很不满意,应该还有更好的方法,知道了来更新。
(update001:下面这个是错误代码,不要参考)
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<cstring> 5 const int maxl=2000009; 6 const int maxn=2000000; 7 using namespace std; 8 int n,m,s,t,p; 9 int lin[maxn],cnt=0; 10 struct str 11 { 12 int y; 13 int z; 14 int next; 15 }e[maxl]; 16 void insert(int x,int y,int z) 17 { 18 cnt++; 19 e[cnt].next=lin[x]; 20 lin[x]=cnt; 21 e[cnt].y=y; 22 e[cnt].z=z; 23 } 24 struct strdis 25 { 26 int x; 27 long long dis; 28 bool operator<(const strdis& a)const{return dis<a.dis;} 29 void set(int xx,int diss){x=xx;dis=diss;} 30 }temp,noww; 31 //bool operator <(const strdis &a,const strdis &b){return a.dis<b.dis;} 32 priority_queue <strdis> q; 33 bool vis[maxn]; 34 long long dis[maxn]; 35 int main() 36 { 37 while(scanf("%d%d%d%d%d",&n,&m,&s,&p,&t)!=EOF) 38 { 39 memset(lin,0,sizeof(lin));cnt=0; 40 memset(vis,0,sizeof(vis)); 41 for(int i=1;i<=m;i++) 42 { 43 int ai,bi,x; 44 scanf("%d%d",&ai,&bi); 45 for(int j=1;j<=bi;j++) 46 { 47 scanf("%d",&x); 48 insert(x,n+i,ai); 49 insert(n+i,x,0); 50 } 51 } 52 temp.set(p,0); 53 q.push(temp); 54 memset(dis,0x3f,sizeof(dis)); 55 dis[p]=0; 56 while(!q.empty()) 57 { 58 noww=q.top(); 59 q.pop(); 60 int nn=noww.x; 61 if(vis[nn])continue; 62 vis[nn]=1; 63 for(int i=lin[nn];i;i=e[i].next) 64 { 65 int u=e[i].y; 66 if(dis[u]>dis[nn]+e[i].z) 67 { 68 dis[u]=dis[nn]+e[i].z; 69 temp.set(u,dis[u]); 70 q.push(temp); 71 } 72 } 73 } 74 if((!vis[s])||(!vis[t])) 75 { 76 printf("Poor guy, you don't have enough Money! "); 77 } 78 else 79 { 80 printf("%lld ",dis[s]+dis[t]); 81 } 82 } 83 84 return 0; 85 }
提交了五次,前两次是忘记去掉打表。。。第三次是复制粘贴的答案字符串里面有不对的字符,第四次是重定义符号弄反了,第五次是没有看到多组数据。
恩,re小王子又回来了。。。。要注意。
哦,既然写了Dijkstra,就要说下如何堆优化,
其实不用担心如何更新堆里边dis的值,没必要,只需要把新的值push进去就好了,因为访问次优的时候,那个点已经vis过了。。。就是空间上浪费点儿。。。不知道怎么解决
update-------------------------------------------------------------------------------------2016.02.11
我的代码比别人的慢四倍,自己怎么找都找不到问题,学长一下就看了出来。。。
原来的代码有一个符号错了,就是重定义小于号那里,因为默认是大根堆,也就是最大的先出来,我们需要最短的,于是需要反着定义
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<cstring> 5 const int maxl=2000009; 6 const int maxn=2000000; 7 using namespace std; 8 int n,m,s,t,p; 9 int lin[maxn],cnt=0; 10 struct str 11 { 12 int y; 13 int z; 14 int next; 15 }e[maxl]; 16 void insert(int x,int y,int z) 17 { 18 cnt++; 19 e[cnt].next=lin[x]; 20 lin[x]=cnt; 21 e[cnt].y=y; 22 e[cnt].z=z; 23 } 24 struct strdis 25 { 26 int x; 27 long long dis; 28 bool operator<(const strdis& a)const{return dis>a.dis;} 29 }noww; 30 //bool operator <(const strdis &a,const strdis &b){return a.dis<b.dis;} 31 priority_queue <strdis> q; 32 bool vis[maxn]; 33 long long dis[maxn]; 34 int main() 35 { 36 while(scanf("%d%d%d%d%d",&n,&m,&s,&p,&t)!=EOF) 37 { 38 memset(lin,0,sizeof(lin));cnt=0; 39 memset(vis,0,sizeof(vis)); 40 for(int i=1;i<=m;i++) 41 { 42 int ai,bi,x; 43 scanf("%d%d",&ai,&bi); 44 for(int j=1;j<=bi;j++) 45 { 46 scanf("%d",&x); 47 insert(x,n+i,ai); 48 insert(n+i,x,0); 49 } 50 } 51 q.push((strdis){p,0}); 52 memset(dis,0x3f,sizeof(dis)); 53 dis[p]=0; 54 while(!q.empty()) 55 { 56 noww=q.top(); 57 q.pop(); 58 int nn=noww.x; 59 if(vis[nn])continue; 60 vis[nn]=1; 61 for(int i=lin[nn];i;i=e[i].next) 62 { 63 int u=e[i].y; 64 if(dis[u]>dis[nn]+e[i].z) 65 { 66 dis[u]=dis[nn]+e[i].z; 67 q.push((strdis){u,dis[u]}); 68 } 69 } 70 } 71 if((!vis[s])||(!vis[t])) 72 { 73 printf("Poor guy, you don't have enough Money! "); 74 } 75 else 76 { 77 printf("%lld ",dis[s]+dis[t]); 78 } 79 } 80 81 return 0; 82 }