【问题描述】
奶牛们没钱了,正在找工作。FJ知道后,希望奶牛们四处转转,碰碰运气。而且他还加了一条要求:一头奶牛在一个城市最多只能赚D(0 < D <= 1000)美圆,然后他必须到另一个城市工作。当然,他可以在别处工作一阵后又回来再多赚D美圆。而且这样往往返返的次数没有限制。
城市间P(1<=P<=150)条单向道路连接,共有N(2<=N<=300)座城市(编号为1..N)。贝西当前在城市S。道路i从城市Ai到城市Bi,在道路上行走不用花费任何费用。
为了帮助贝西,FJ让他使用他的私人飞机,飞机有F(1<=F<=350)条航线,第j条航线是从城市Aj到城市Bj,费用是Tj(1<=Tj<=50000)美圆,如果贝西手中没有现钱,可以用以后赚的钱来付机票钱,贝西可以选择任何时候,任何城市退休。
如果在工作时间上不作限制,贝西总共可以赚多少钱呢?如果赚的钱不会出现限制,就输出INF。
【输入格式】
第1行:5个空格分开的整数D,P,N,F,S
第2到第P+1行:第i+1行包含2个空格分开的整数,表示从Ai到Bi的单向道路。
第P+2到P+F+1行:第P+j包括3个空格分开的整数,表示一条从Aj到Bj的单向航线,费用为Tj
【输出格式】
在上述规则下得最多可赚得钱数。
【输入样例】
【样例1】
100 3 5 2 1
1 5
2 3
1 4
5 2 150
2 5 120
【样例2】
100 3 5 2 1
1 5
2 3
1 4
5 2 70
2 5 120
【输出样例】
【样例1】
250
【样例2】
INF
【数据范围】
N<=300
这道题要用一种重要的思想,就是把一个点拆成2个点(i和i+n),这样就可以把点权换成边权,然后直接进行spfa就可以了。
另一种怪异方法
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<queue>
using namespace std;
const int maxn=605;
const int inf=200000000;
vector<int>g[maxn],w[maxn];
int D,p,n,f,s,d[maxn],vis[maxn]={0},qn[maxn]={0},ok=0;
void init()
{
scanf("%d%d%d%d%d",&D,&p,&n,&f,&s);
for(int i=1;i<=n;i++)
{
g[i].push_back(i+n);//拆点。
w[i].push_back(D);
}
for(int i=1;i<=p;i++)
{
int x,y;
scanf("%d%d",&x,&y);
g[x+n].push_back(y);//i进,i+n出。
w[x+n].push_back(0);
}
for(int i=1;i<=f;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
g[x+n].push_back(y);
w[x+n].push_back(-z);
}
for(int i=1;i<=2*n;i++)
d[i]=-inf;
}
void spfa()
{
queue<int>q;
q.push(s);
d[s]=0;
vis[s]=1;
qn[s]++;
while(!q.empty())
{
int i=q.front();
q.pop();
vis[i]=0;
for(int k=0;k<g[i].size();k++)
{
int j=g[i][k],c=w[i][k];
if(d[i]+c<=d[j]) continue;
d[j]=d[i]+c;
if(vis[j]) continue;
vis[j]=1;
qn[j]++;
if(qn[j]>n)
{
ok=1;
return;
}
q.push(j);
}
}
}
int main()
{
init();
spfa();
if(ok)
printf("INF");
else
{
int ans=0;
for(int i=1;i<=2*n;i++)
ans=max(ans,d[i]);
printf("%d",ans);
}
return 0;
}