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");  
    }    
} 

 

posted @ 2017-02-24 12:13  despair_ghost  阅读(182)  评论(0编辑  收藏  举报