问题描述
达卡市一天比一天拥挤嘈杂。某些道路总是堵在路上。为了说服人们避开最短的路线,从而避开拥挤的道路,到达目的地,市当局制定了一个新的计划。城市的每个交叉口都用一个正整数(≤20)表示交叉口的繁忙程度。每当有人从一个路口(源路口)走到另一个路口(目的地路口),城市当局就会从旅行者那里获得数量(目的地繁忙度-源繁忙度)3(即差值的立方)。权威已经指定你去找出当一个聪明的人从某个交点(零点)到其他几个交点时能挣到的最低工资总额。
输入
输入以整数T(≤50)开始,表示测试用例的数量。
每个case包含一个空行和一个整数n (1 < n≤200),表示连接的数量。下一行包含n个整数,分别表示从1到n的连接的繁忙程度。下一行包含一个整数m,即城市中的道路数量。接下来的m行(每条路一条)包含两个连接点——对应的路连接的数字(源、目的)(所有的路都是单向的)。下一行包含整数q,即查询的数量。接下来的q行各包含一个目的地连接号。从一个路口到另一个路口最多只能有一条直路。
输出
对于每种情况,在一行中打印案例号。然后打印q行,每个查询一个行,每个行包含从结1(零点)到给定结的最低总收入。但是,对于总收益小于3的查询,或者如果不能从0点到达目的地,则打印一个’?’。
Sample Input
2
5
6 7 8 9 10
6
1 2
2 3
3 4
1 5
5 4
4 5
2
4
5
2
10 10
1
1 2
1
2
Sample Output
Case 1:
3
4
Case 2:
?
分析:
第一次写标记负环节点的题,记录一下
(目的地繁忙度-源繁忙度)3显然可能会导致出现负环
可以用bellmanford或者spfa找到负环
并且在找到负环的时候DFS标记负环上的所有节点
(负环权值可以一直缩小,值显然小于3,所以输出’?’)
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#define set0(a) memset(a,0,sizeof(a))
#define set1(a) memset(a,-1,sizeof(a))
#define setinf(a) memset(a,0x3f3f3f3f,sizeof(a))
#define ll long long
#define P pair<int,int>
#define mp(a,b) make_pair(a,b)
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int maxm=5e4+5;
int head[maxm],to[maxm],w[maxm],nt[maxm];
int cnt;
int a[maxm],d[maxm],mark[maxm],num[maxm];
bool cir[maxm];
void add(int x,int y,int z){
cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
}
void init(){
set1(head);
cnt=0;
set0(cir);
set0(mark);
set0(num);
setinf(d);
}
void dfs(int x){//把负环上的点全部标记
cir[x]=1;//标记
for(int i=head[x];i!=-1;i=nt[i]){
int v=to[i];
if(!cir[v]){
dfs(v);
}
}
}
void spfa(int st,int n){
queue<int>q;
q.push(st);
d[st]=0;
mark[st]=1;
num[st]=1;
while(!q.empty()){
int x=q.front();
q.pop();
mark[x]=0;
if(cir[x])continue;//负环跳过
for(int i=head[x];i!=-1;i=nt[i]){
int v=to[i];
if(cir[v])continue;//不走负环,跳过
if(d[v]>d[x]+w[i]){
d[v]=d[x]+w[i];
if(!mark[v]){
mark[v]=1;
q.push(v);
num[v]++;
if(num[v]>=n){
dfs(v);//出现负环就dfs标记
}
}
}
}
}
}
int main(){
int T;
int cas=1;
scanf("%d",&T);
while(T--){
init();
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int m;
scanf("%d",&m);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
int temp=a[y]-a[x];
add(x,y,temp*temp*temp);
}
spfa(1,n);
int q;
scanf("%d",&q);
printf("Case %d:\n",cas++);
while(q--){
int t;
scanf("%d",&t);
if(cir[t]||d[t]==inf||d[t]<3){//1.负环上 2.无法到达 3.答案小于三
printf("?\n");
}else{
printf("%d\n",d[t]);
}
}
}
return 0;
}