洛谷P2648 赚钱
题目描述
zzy现在决定环游中国,顺便赚点钱。zzy在一个城市最多只能赚D元,然后他可以选择退休也就是停止赚钱,或者去其它城市工作。当然,他可以在别处工作一阵子后又回到原来的城市再赚D元。这样的往返次数是没有任何限制的。
城市间有P条单向路径连接,共有C座城市,编号从1到C。路径i从城市Ai到城市Bi,在路径行走上不用任何花费。
zzy还可以乘飞机从某个城市飞到另一个城市。共有F条单向的航线,第i条航线是从城市Ji飞到另一座城市Ki,费用是Ti元。假如zzy身上没有现钱,他可以用以后赚的钱来付机票钱。
zzy可以从任何一个城市出发开始赚钱,并且选择在任何时候、任何城市退休。现在zzy想要知道,如果在工作时间上不做限制,那么zzy共可以赚多少钱呢?如果赚的钱也不会出现限制,那么就输出orz。
输入格式
第一行,4个用空格分开的正整数,D,P,C,F。
第二行到P+1行,第i+1行包含2个用空格分开的整数,表示一条从城市Ai到城市Bi的单向路径。
接下来的F行,每行3个用空格分开的正整数,表示一条从城市Ji到城市Ki的单向航线,费用为Ti。
输出格式
如果zzy赚的钱没有限制,输出orz。如果有限制,那么就输出在给定的规则下zzy最多可以赚到的钱数。
输入输出样例
输入 #1
100 3 5 2 1 5 2 3 1 4 5 2 150 2 5 120
输出 #1
250
说明/提示
对于100%的数据,1<=D<=1000,1<=P<=200,2<=C<=300,1<=F<=400。
解题思路
简单理解一下题目,每个点都有一个值(赚的钱D),有P个免费道路,还有F个飞机航线。问我们最多可以赚多少钱,如果不受时间限制的话,可以一直赚钱,那就输出“orz”,一直赚钱的话,很可能就是出现了环。
所以,我们可以把每个点的值下放到连接的边上,也就是说,把免费的道路变成能给你钱的道路,把飞机航线变成赚到的钱减去机票钱后得到的价格。这样,我们就把杂乱的点权和边权,全部变成了边权,题目要求我们求出最多能赚多少钱,因为可以从任意一个点开始,所以是多源最长路问题,我们用floyd解决。
这里要特殊注意,出现环的情况,我们可以跑两次最长路,看结果是否相同,不相同就证明有环,可以一直赚钱。否则直接输出就可以了。
代码示例
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int d,p,c,f;
int g[310][310];
void floyd(){
for(int k=1;k<=c;k++){
for(int i=1;i<=c;i++){
for(int j=1;j<=c;j++){
g[i][j]=max(g[i][j],g[i][k]+g[k][j]);
}
}
}
}
int find(){
int x=0;
for(int i=1;i<=c;i++)
for(int j=1;j<=c;j++)
x=max(x,g[i][j]);
return x;
}
int main(){
cin>>d>>p>>c>>f;
memset(g,200,sizeof(g));
int x,y,z;
for(int i=1;i<=p;i++){
cin>>x>>y;
g[x][y]=d;
}
for(int i=1;i<=f;i++){
cin>>x>>y>>z;
g[x][y]=d-z;
}
floyd();
int ans1=find();
floyd();
int ans2=find();
if(ans1!=ans2) cout<<"orz"<<endl;
else cout<<ans1+d<<endl;
return 0;
}
当然,你如果想从每个点跑一次spfa也可以(数据比较和善)