题目大意:
一个有向图,给你n个点,m条双向路径,以及t条虫洞。每条路径描述在两个点ab之间移动需要时间v。每条虫洞描述从a到b需要时间-v(类似于时空穿越)。现在就问你,一个人能否从某个点开始,通过若干次虫洞和路径,在他出发之前的每个时刻回到出发点。注意:两个点之间有可能有多条路径!
分析:
相当于找一个有向图中的负权回路。关于两个点之间可能有多条路径这个事情,在存图的时候要好好考虑一下。比如两个点AB之间有两条路径a,b(va>vb)以及一条虫洞单向c,那么,为了形成负权环路,我的存储方式应该是:map [ A ] [ B ] = – vc ; map [ B ] [ A ] = vb; 因为如果某条负权回路经过边a,那么,那这条边改成b也一定是负权回路(va>vb),如果经过b,那么,把这条边改成c也一定是负权回路(vb>0>-vc)。所以我选择先把合适的值存到map数组里,在遍历map数组来得到所有的有向边。时间复杂度: O(n2)
代码:
#include<iostream>
#include<string.h>
#define inf 0x3f3f3f3f
using namespace std;
struct edge
{
int s;
int e;
int v;
}l[2600]={0};
int num=1;
int map[550][550];
int n,m,t;
int flag=0;
void my_Bellman_Ford()
{
int dis[550]={0};
dis[1]=0;
flag=0;
for(int k=1;k<n;k++)
{
for(int i=1;i<=num;i++)
{
if(dis[l[i].e]>dis[l[i].s]+l[i].v)
{
dis[l[i].e]=dis[l[i].s]+l[i].v;
}
}
}
for(int i=1;i<=num;i++)
{
if(dis[l[i].e]>dis[l[i].s]+l[i].v)
{
flag=1;
return;
}
}
}
int main()
{
int test;
cin>>test;
while(test--)
{
cin>>n>>m>>t;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
map[i][j]=inf;
}
}
int ls,le,lv;
for(int i=1;i<=m;i++)
{
cin>>ls>>le>>lv;
if(map[ls][le]>lv)
{
map[ls][le]=lv;
map[le][ls]=lv;
}
}
for(int i=1;i<=t;i++)
{
cin>>ls>>le>>lv;
map[ls][le]=-lv;
}
num=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(map[i][j]!=inf)
{
l[num].s=i;
l[num].e=j;
l[num].v=map[i][j];
num++;
}
}
}
num--;
my_Bellman_Ford();
if(flag==1)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
后记:
这道题本来是训练计划里的一道,当时并没做出来,然后就莫名其妙给忘记了,现在回来一看,突然感觉简单了好多。变强中···