题意:玩家能量初始值有100,有100个房间,每个房间有一个大于-100小于100的能量值,每个房间与其他一些房间单向联通,玩家走到一个房间后能量加上房间的能量值,房间可以重复进入。若玩家的能量小于等于0,玩家输,若玩家到达终点,玩家赢。
以房间为点,从房间到房间建有向边,边的权值为进入的房间的能量值。求初始点到各点的剩余最大能量,注意如果出现环,要判断这个环是否与终点联通。判断连通性需要用floyd进行预处理。
判环应该用bellman,此题的数据比较弱,用spfa的代码也能AC,但应该是错的。
举个例子:同时出现两个环,一个与终点联通,一个不连通。若spfa先判断不连通的环时,会直接返回玩家输,而正确结果应该是赢。
先给出bellman_ford代码:
#include<cstdio> #include<cmath> #include<cstring> #include<iostream> #define N 110 #define M 11000 #define INF 0x7ffffff using namespace std; int r[N][N],n,val[N],d[N],cnt; struct node { int u,v; }e[M]; void floyd() { for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) r[i][j]=max(r[i][j],r[i][k]&r[k][j]); } int bellman_ford(int s,int t) { for(int i=1;i<=n;i++) d[i]=-INF; d[s]=100; for(int i=0;i<n;i++) { for(int j=0;j<cnt;j++) { int u=e[j].u,v=e[j].v; if(d[u]+val[v]>0) d[v]=max(d[v],d[u]+val[v]); } } for(int i=0;i<cnt;i++) { int u=e[i].u,v=e[i].v; if(d[u]+val[v]>0&&d[v]<d[u]+val[v]&&r[v][t]) return 1; } return d[t]>0; } int main() { while(~scanf("%d",&n)&&n!=-1) { cnt=0; memset(r,0,sizeof(r)); for(int i=1;i<=n;i++) { int nn; scanf("%d%d",&val[i],&nn); for(int j=0;j<nn;j++) { int c; scanf("%d",&c); e[cnt].u=i; e[cnt++].v=c; r[i][c]=1; } } floyd(); if(bellman_ford(1,n)) cout<<"winnable"<<endl; else cout<<"hopeless"<<endl; } }
AC的spfa代码:
#include <iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #define N 110 #define INF 0x7ffffff using namespace std; int n,val[N],mp[N][N],d[N],v[N],num[N],r[N][N]; void floyd() { for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) r[i][j]=max(r[i][j],r[i][k]&r[k][j]); } int spfa(int s,int t) { for(int i=1;i<=n;i++) { num[i]=0; d[i]=-INF; v[i]=0; } queue<int> q; q.push(s); v[s]=1; d[s]=100; num[s]++; while(!q.empty()) { int c=q.front(); q.pop(); v[c]=0; for(int i=1;i<=n;i++) { if(d[c]+mp[c][i]>0&&d[i]<d[c]+mp[c][i]) { d[i]=d[c]+mp[c][i]; if(!v[i]) { if(++num[i]>=n) return r[i][t]; v[i]=1,q.push(i); } } } } if(d[t]>0) return 1; return 0; } int main() { while(~scanf("%d",&n)&&n!=-1) { memset(mp,0,sizeof(mp)); memset(r,0,sizeof(r)); for(int i=1;i<=n;i++) { int nn; scanf("%d%d",&val[i],&nn); for(int j=0;j<nn;j++) { int c; scanf("%d",&c); mp[i][c]=1; r[i][c]=1; } } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { if(mp[i][j]==0) mp[i][j]=-INF; else mp[i][j]*=val[j]; } floyd(); if(spfa(1,n)) cout<<"winnable"<<endl; else cout<<"hopeless"<<endl; } }