题目
题解思路
1. 由于是立方差,所以存在负环,迪杰斯特拉就失效了。SPFA启动!
2. 对于负环上的点,我们要进行染色处理(当这个点入队的次数大于N时,即用DFS跑出这个点能延申的所有区域)
3. 负环,又叫负权回路,负权环,指的是一个图中存在一个环,里面包含的边的边权总和<0。
4. 用染色法,将出现负环的点的连通分量全部染色,遇到被染色的点直接跳过!
5. 多组样例,一定不要忘记初始化!!!
关于 染色法
因为存在负环导致1 2 3 4 的值都是假的 ,进而导致 5的值也是假的(尽管5不是环中的元素),因为从1开始走必须要经过2或者1才能到5,所以只要是负环中的元素能到达的点,全都是假的值。用DFS搜索能到达的点然后标记即可!
下面展示一个不是和原点直接相连的负环,2 3 4 5 构成负环 ,6本来不是负环中的元素也受到了3的影响,变成了负值,所以,只要是负环元素相连的点都是假的答案,都必须染色来去掉!
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int INF = 0x3f3f3f3f;
int a[210];
struct bian
{
int w;
int z;
};
vector <bian> head[210];
bool vis[210];
bool vis2[210];
int dis[210];
int cnt[210];
int n,m;
void dfs(int x)
{
for (int i = 0 ; i < head[x].size() ;i++ )
{
int tmp = head[x][i].z;
if (vis2[tmp])
continue;
vis2[tmp] = 1;
dfs(tmp);
}
}
void spfa ()
{
memset(vis,0,sizeof(vis));
memset(vis2,0,sizeof(vis2));
memset(cnt,0,sizeof(cnt));
for (int i = 1 ;i <= n ; i++ )
dis[i] = INF ;
queue <int> q;
dis[1] = 0;
cnt[1]++;
q.push(1);
vis[1] = 1;
int tmm;
while(!q.empty())
{
tmm = q.front();
q.pop();
vis[tmm] = 0;
if (vis2[tmm])
continue;
for ( int i = 0 ; i < head[tmm].size() ; i++ )
{
/* int tmp = dis[tmm] + head[tmm][i].w;
printf("tmm = %d " ,tmm);
printf("tmp = %d " ,tmp);
printf(" %d \n",dis[head[tmm][i].z]); */
if (dis [head[tmm][i].z] > dis[tmm] + head[tmm][i].w )
{
dis [head[tmm][i].z] = dis[tmm] + head[tmm][i].w;
if (cnt[head[tmm][i].z] > n)
{
dfs(head[tmm][i].z);
continue;
}
if (vis[head[tmm][i].z] == 0)
{
// printf("入队的点为%d \n",head[tmm][i].z);
q.push(head[tmm][i].z);
cnt[head[tmm][i].z]++;
vis[head[tmm][i].z] = 1;
}
}
}
/* for (int i = 1 ;i <= n ; i++ )
cout<<dis[i]<<" ";
cout<<"\n"; */
}
}
int main ()
{
int f,sum = 1;;
cin>>f;
while(f--)
{
cin>>n;
for (int i = 1 ; i <= n ; i++ )
cin>>a[i];
cin>>m;
bian t;
for (int i = 1 ; i <= m ; i++ )
{
int s1,s2;
cin>>s1>>s2;
t.w = -(a[s1]-a[s2])*(a[s1]-a[s2])*(a[s1]-a[s2]);
t.z = s2;
head[s1].push_back(t);
}
spfa();
int p;
cin>>p;
cout<<"Case "<<sum<<":\n";
for (int i = 1 ;i <= p ; i++ )
{
int pot;
cin>>pot;
if ( dis[pot] < INF && cnt[pot] <= n && dis[pot] >= 3 && vis2[pot] == 0 )
{
cout<<dis[pot]<<"\n";
}else
cout<<"?\n";
}
sum++;
for (int i = 1 ;i <= n ; i++ )
head[i].clear();
}
return 0;
}