大致题意:
给出一个由n个房子,由若干的单向路连接起来,每个房子都有一个权值,意味着进入这个房子得到或者消耗的能量。把你放在1点,给你100点的初始能量。现在问你能否到达n点且到达时权值大于0.
大致思路:
很好的题目,参考了小媛神的思路 Orz。首先用spfa求最长路,同时判定是否存在正圈,再用floyd求出传递闭包。如果spfa求出的dis[1,n]大于0。或者从起点可以到达某个正圈,且这个正圈可以到达终点,则认为存在可行方案。
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
const int nMax=1050;
const int mMax=1000050;
const int inf=1<<28;
struct{
int u,v,next;
int w;
}edge[mMax];
int n, k, edgeHead[nMax];
int dis[nMax],num[nMax];
int stack[nMax],m;
bool vis[nMax];
void addedge(int a,int b,int w){
edge[k].w = w;
edge[k].u=a;
edge[k].v=b;
edge[k].next=edgeHead[a];
edgeHead[a]=k;k++;
}
int eng[nMax];
bool map[nMax][nMax];
bool spfa(int s){
int i,top=0;
memset(vis,0,sizeof(vis));
for(i=1;i<=n;i++)dis[i]=0;
dis[s]=100;
stack[++top]=s;
vis[s]=true;
num[s]++;
while(top){
int u=stack[top--];
vis[u]=false; /
if(num[u]>n)break;
for(i=edgeHead[u];i!=0;i=edge[i].next){
int v=edge[i].v;
if(dis[v]<dis[u]+edge[i].w){
dis[v]=dis[u]+edge[i].w;
if(!vis[v]){
vis[v]=true;
stack[++top] = v;
num[v]++;
}
}
}
}
if(dis[n]>0){
return 1;
}
return 0;
}
int main(){
int i,j,a,b,c,s,t;
while(scanf("%d",&n)&&n>0){
k=1;
memset(map,0,sizeof(map));
memset(edgeHead,0,sizeof(edgeHead));
for(i=1;i<=n;i++){
scanf("%d%d",&eng[i],&m);
for(j=0;j<m;j++){
scanf("%d",&a);
addedge(i,a,0);
map[i][a]=1;
}
}
for(i=1;i<k;i++){
edge[i].w=eng[edge[i].v];
}
// for(i=1;i<k;i++){
// cout<<"fuck "<<edge[i].u<<" "<<edge[i].v<<" "<<edge[i].w<<"\n";
// }
memset(num,0,sizeof(num));
if(spfa(1)){
printf("winnable\n");
}
else{
bool flag=0;
for(k=1;k<=n;k++){
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
if(map[i][k]&&map[k][j]){
map[i][j]=1;
}
}
}
}
for(i=1;i<=n;i++){
if(map[1][i]&&map[i][n]){
if(num[i]>n){
flag=1;
break;
}
}
}
if(flag){
printf("winnable\n");
}
else{
printf("hopeless\n");
}
}
}
return 0;
}