C语言BFS(5)___TT与魔法师(swust oj 2464)

来源:互联网 发布:win7网络共享 编辑:程序博客网 时间:2024/06/05 00:29

    TT生活在一个充满魔法的国度,为了便于管理,国王请魔法师在一些重要的城市之间造出了“彩虹桥”!彩虹桥的特殊之处在于,可以从桥的一头瞬间移动到另一头。国王还请魔法师为彩虹桥设计出了通行证,通行证大致分成A,B,C三种,彩虹桥也对应A,B,C三种,每个彩虹桥可以识别的通行证最多为三种,每个人都拥有一个唯一类型的通行证,拥有通行证的人可以在对应的彩虹桥两头来回穿梭,如拥有A通行证的人只可以穿梭于可识别A通行证的彩虹桥。    一天,TT因为有急事需要从城市1穿梭到城市N,显然,一种类型的通行证可能不能使他顺利的从城市1到城市N。于是他请巫师帮忙看能不能设计一张万能的通行证,使他可以穿梭任意彩虹桥。巫师心力交瘁,终于设计出一种可以变换的通行证,这张通行证被巫师用魔法药水浸泡了七七四十九天,凝结了巫师的全部智慧,终于拥有了神奇的变换功能,使得它在每次使用以后会自动的变成另一种类型的通行证,变换的顺序为A->B->C->A…..如此循环。假设刚开始的类型为A。请你告诉TT如果他利用这张魔法卡片能不能顺利的从1点到N点。当然,由于每次传送都需要支付一定费用,所以在有多条路径存在的情况下,请选择花费最少的一条。
   第一行为测试的组数T,每组测试样例第一行输入2个数N,M代表N个城市之间有M条彩虹桥(1<=N<=1000,0<=M<=N*N)。接下来M行,每行四个数字S T L C(S!=T,1<=L<=1000,1<=C<=3)代表城市S和T之间(1<=S,T<=N)需要L的通行费和C型(为了方便,我们用1,2,3分别代表通行证A,B,C的类型)通行证(所有数字均是正整数)。
如果TT能顺利的从城市1到城市N,输出最终的最小花费,否者输出-1
24 31 2 1 12 3 2 23 4 3 3 4 31 2 1 12 3 2 23 4 3 2
6 -1
原题链接: http://www.oj.swust.edu.cn/problem/show/2464


分析:
最开始想的是用记忆化搜索,然后无情的WA的很多次,然后只能随机生成数据跟AC代码对比跑,终于跑了几千组数据找到了原因,记忆化搜索在这道题出现的弊端就是如果路径顺序读取问题可能会导致过某个点A后到达另一个点B,假如存在一条从B到A再到N点的路径,这时候因为先到了A点,A被标记了,然后B就不能通过A到达N点,这个时候B的数据就被记忆化为不可到达终点。然而标记又不能取消,不然就会在环状地图进行死循环。这个问题只是记忆化搜索的问题之一,然而代码改了很多遍还是Wa.
下面贴出记忆化搜索的代码:
#include <cstdio>#include <cstring>#include <vector>#include <iostream>#include <queue>#include <stdlib.h>using namespace std;int memory[1001][1001][4],n,m;int mpt[1001][1001][4],money[1001][1001][4];bool book[1001][1001][4]; vector<int>way[1001]; int Min(int a,int b){return (a>b)? b:a;}int dfs(int pre,int step,int k){if(step==n)return 0;if(memory[pre][step][k])return memory[pre][step][k];book[pre][step][k]=true;int i,ans=999999999;for(i=0;i<way[step].size();i++){//cout<<step<<"→"<< way[step][i]<<endl;//printf("book[%d][%d]=%d\n",way[step][i],k%3+1,book[way[step][i]][k%3+1]);if(mpt[step][way[step][i]][k]==0 || book[step][way[step][i]][k%3+1]==true)continue;//cout<<step<<"→"<< way[step][i]<<" K:"<<k<<endl;ans=Min(ans,dfs(step,way[step][i],k%3+1)+money[step][way[step][i]][k]);}book[pre][step][k]=false;//printf("memort[%d][%d]=%d\n",step,k,ans); if(ans!=999999999) memory[pre][step][k]=ans;return ans;} void init(){int i;for(i=0;i<1001;i++) way[i].clear();memset(memory,0,sizeof(memory));memset(money,0,sizeof(money));memset(mpt,0,sizeof(mpt)); memset(book,0,sizeof(book));}int main(){int i,j,v,u,k,w,t,ans;cin>>t;while(t--){cin>>n>>m;init();for(i=0;i<m;i++){scanf("%d%d%d%d",&v,&u,&w,&k);if(mpt[v][u][1]==0 && mpt[v][u][2]==0 && mpt[v][u][3]==0){way[v].push_back(u);way[u].push_back(v);}if(money[v][u][k]==0) money[v][u][k]=w,money[u][v][k]=w;else{money[v][u][k]=Min(money[v][u][k],w);money[u][v][k]=Min(money[u][v][k],w);}mpt[u][v][k]=mpt[v][u][k]=1; }ans=dfs(0,1,1);if(ans!=999999999)printf("%d\n",ans);else printf("-1\n");} return 0;  } 



然后用SPFA来做这道题:
#include <cstdio>#include <cstring>#include <vector>#include <iostream>#include <queue>#include <stdlib.h>using namespace std;struct node{int data,v,w;};struct node1{int step,v;};int n,m;int visit[1001][4],dis[1001][4];  //visit数组是记录当前状态 [节点编号][通行证编号]是否在队列中.vector<node>mpt[1001];void SPFA(){node1 s,e;int i,j,ss;queue<node1>team;s.step=1;s.v=1;team.push(s);visit[1][1]=1;while(team.size()){   //用队列中的状态去更新最小花费.s=team.front();team.pop();visit[s.step][s.v]=0;ss=mpt[s.step].size();for(i=0;i<ss;i++){if(mpt[s.step][i].v!=s.v)continue;if(dis[s.step][s.v]+mpt[s.step][i].w>=dis[mpt[s.step][i].data][s.v%3+1])continue; dis[mpt[s.step][i].data][s.v%3+1]=dis[s.step][s.v]+mpt[s.step][i].w;  //松弛if(visit[mpt[s.step][i].data][s.v%3+1]!=0)continue;e.step=mpt[s.step][i].data;e.v=s.v%3+1;visit[e.step][e.v]=1;team.push(e);}}int min=1999999999;for(i=1;i<4;i++)if(min>dis[n][i])min=dis[n][i];if(min!=1999999999)cout<<min<<endl;else cout<<"-1"<<endl; } int main(){int i,j,t,x,y;cin>>t;while(t--){cin>>n>>m;memset(visit,0,sizeof(visit));for(i=1;i<=n;i++)for(j=0;j<4;j++)dis[i][j]=1999999999;dis[1][1]=0;for(i=0;i<1001;i++)mpt[i].clear(); for(i=0;i<m;i++){node s;cin>>x>>y>>s.w>>s.v;s.data=x;mpt[y].push_back(s);s.data=y;mpt[x].push_back(s);}SPFA();}return 0;}

如果有大神能用记忆化搜索做出来麻烦贴在下面的评论一下,万分感谢!!!!



0 0
原创粉丝点击