链接:http://acm.hdu.edu.cn/showproblem.php?pid=1317
题意:
有 N 个房间, 编号从 1 到 N 。
每次进入一个房间, 能量值可能增加也可能减少问:从第一个房间开始走, 给你 100 个能量值。
问你是否能走到第 N 个房间。
第一行 N 输入房间的个数
然后下面 N 行数据:
第 i 行数据的第一个表示进入该房间得到的能量【可正可负】
第二个表示从该房间出发能到达的房间个数 num
剩下 num 个数表示可以到达的房间编号
思路:
如果图中存在正环, 那么就可以不停的走这个环来增加能量,
如果环中的点能到达 N 那么肯定是赢了。。。
但是由于这个先入为主的思想,开始很容易的就让我忽略了,这题的本质是到达 N 的时候还有能量。
然后就是各种不注意,各种 WA 的血泪史。。。。然后网上各种高深的题解。
直到看到了一篇用 Bellman_ford 写的
加边建图的过程同时记录连通性, 先判断 1 与 N 在不考虑能量的时候是否连通【不判断也可以,只是个无关紧要的优化】
加边建图的过程同时记录连通性, 先判断 1 与 N 在不考虑能量的时候是否连通【不判断也可以,只是个无关紧要的优化】
然后就是套用 Bellman_ford() 判断是否有正环
注意:当不存在正环的时候, 不要像以前用这个算法时直接跳出
因为我们的主要目的不是判断正环,而是要使得到达 N 还有能量。
那么赢的可能性就两种了:
1.没有正环, 但是通过Bellman_ford() 的松弛操作, 到 N 的能量值 > 0
2.存在正环, 正环中的点, 能到达终点。
注意:当不存在正环的时候, 不要像以前用这个算法时直接跳出
因为我们的主要目的不是判断正环,而是要使得到达 N 还有能量。
那么赢的可能性就两种了:
1.没有正环, 但是通过Bellman_ford() 的松弛操作, 到 N 的能量值 > 0
2.存在正环, 正环中的点, 能到达终点。
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
const int maxn = 110;
const int INF = 10000000000;
int w[maxn][maxn]; // 判断图的连通性
int en[maxn]; //进入该点的能量值
int d[maxn]; //每一点的能量值
int n,m; //n 个点, m 条边
struct Edge{
int u,v;
}edge[maxn*maxn];
void floyd() // 有向图的传递闭包
{
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
w[i][j] = w[i][j] || (w[i][k] && w[k][j]); //不要写错了 WA 的都是泪。。。
}
bool Bellman_ford()
{
for(int i = 1; i <= n; i++) d[i] = -INF;
d[1] = 100; // 初始第一个点有 100 个能量
for(int i = 1; i < n; i++)
{
for(int j = 0; j < m; j++)
{
int u = edge[j].u;
int v = edge[j].v;
if(d[v] < d[u]+en[v] && d[u]+en[v] > 0) //松弛
d[v] = d[u]+en[v]; //是加上点的权。。。
} //注意:不能像以前一样不能松弛了,就直接返回 false 因为判断正环的目的是使 d[n] > 0
}
for(int i = 0; i < m; i++)
{
int u = edge[i].u;
int v = edge[i].v;
if(d[v] < d[u]+en[v] && d[u]+en[v] > 0) //如果存在正环
if(w[v][n]) //正环中的点能够到达终点
return true;
}
return d[n]>0; // 不存在正环, 判断能否依靠 100 个能量值到达终点
}
int main()
{
while(scanf("%d", &n) != EOF)
{
if(n == -1) break;
m = 0; // 初始化边
memset(w, 0, sizeof(w));
memset(en, 0, sizeof(en));
for(int i = 1; i <= n; i++) w[i][i] = 1;
int num;
for(int i = 1; i <= n; i++)
{
int v;
scanf("%d%d", &en[i], &num);
while(num--) //注意是单向的
{
scanf("%d", &v);
edge[m].u = i;
edge[m++].v = v;
w[i][v] = 1; // 有向图, 不要傻逼的加上 w[v][i] = 1
}
}
floyd(); // 考查有向图的连通性
/*
if(!w[1][n])
{
printf("hopeless\n"); continue;
}*/
if(Bellman_ford()) printf("winnable\n");
else printf("hopeless\n");
}
return 0;
}
原文出处:
点击链接