PKU 1932 XYZZY(Floyd+Bellman||Spfa+Floyd)
题目大意:原题链接
给你一张图,初始你在房间1,初始生命值为100,进入每个房间会加上那个房间的生命(可能为负),问是否能到达房间n。(要求进入每个房间后生命值都大于0)
解题思路:
解法一:Floyd+Bellman
1.Floyd先判断图是否连通,不连通则直接失败
2.Bellman Ford然后跑最长路,判断是否有正环或者有正通路
#include<cstdio> #include<cstring> using namespace std; int n,val[110],d[110]; bool graph[110][110],link[110][110]; int main() { int i,j,k,cnt,id; while(scanf("%d",&n),n!=-1){ memset(d,-1,sizeof(d)); memset(val,0,sizeof(val)); memset(link,false,sizeof(link)); memset(graph,false,sizeof(graph)); for(i=1;i<=n;i++){ scanf("%d%d",&val[i],&cnt); while(cnt--){ scanf("%d",&id); graph[i][id]=true; link[i][id]=true;//用于判断任意两点间的连通性 } } //Floyd判断连通性 for(k=1;k<=n;k++){ for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ if(link[i][k]&&link[k][j]) link[i][j]=true; } } } if(!link[1][n]){ printf("hopeless\n"); continue; }//必须加上这个 link[n][n]=true; //Bellman-ford求最长路径 d[1]=100; for(k=1;k<=n;k++){//遍历每一个节点 bool sign=true; for(i=1;i<=n;i++){//遍历每一条边 for(j=1;j<=n;j++){//j,n必须是连通的,并且前一个点d[i]为正 if(graph[i][j]&&link[j][n]&&d[j]<d[i]+val[j]&&d[i]>0){ d[j]=d[i]+val[j]; sign=false; } } }//如果d[j]已经不能更新了,说明最长路已经查找完毕,即不存在正环,退出循环 if(sign) break; } if(k>n||d[n]>0) printf("winnable\n");//有正环或者有正通路 else printf("hopeless\n"); } }
解法二:Spfa+Floyd
1.Spfa先判正环+跑最长路,以及判断是否存在正通路
2.若d[n]<=0,则Floyd然后根据存在正环以及是否连通起点和终点判断是否会成功到达n点
#include<cstdio> #include<cstring> #include<queue> #define inf 0x3f3f3f3f using namespace std; int n,p,num[110];//num[u]表示u入队次数 int val[110],d[110]; bool in[110],link[110][110]; queue<int> que; bool Spfa_floyd(int u) { for(int i=1;i<=n;i++){ d[i]=-inf; in[i]=0,num[i]=0; } while(!que.empty()) que.pop();//que定义为全局变量就得清空队列 que.push(u); in[u]=true; num[u]=1,d[u]=100; while(!que.empty()){ u=que.front(); que.pop(); in[u]=false; if(num[u]>n){ p=u; break;//Spfa某节点入队次数大于n则有正环 } for(int v=1;v<=n;v++){ if(link[u][v]&&d[u]+val[v]>0&&d[u]+val[v]>d[v]){ d[v]=d[u]+val[v];//最长路 if(!in[v]){ que.push(v); in[v]=true; num[v]++; } } } } if(d[n]>0) return true; else{ for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(link[i][k]&&link[k][j]) link[i][j]=true; if(num[p]>n&&link[1][p]&&link[p][n]) return true;//如果存在正环,并且连接起点和终点 return false; } } int main() { while(scanf("%d",&n),n!=-1){ memset(link,0,sizeof(link)); memset(val,0,sizeof(val)); for(int i=1;i<=n;i++){ int cnt,id; scanf("%d%d",&val[i],&cnt); for(int j=1;j<=cnt;j++){ scanf("%d",&id); link[i][id]=true; } } if(Spfa_floyd(1)) puts("winnable"); else puts("hopeless"); } }