- POJ 2983 - Is the Information Reliable?
- Time: 3000MS
- Memory: 131072K
- 难度: 中级
- 分类: 差分约束
问题描述
给出M个表达式,判断这些信息是否可靠。
解题思路
差分约束+Bellman-Ford(建议用优化的Bellman-Ford)
设 dist[i]
为超级源点到i点的距离,则
建立 <=
的差分系统:
由于 P A B X 指“确定A到B的距离(边权)为X”
从 P A B X 得到的差分系统为
dist[A] - dist[B] >= X && dist[A] - dist[B] <= X
等价于
dist[B] <= dist[A] - X && dist[A] <= dist[B] + X
则 if(dist[B] > dist[A]-X)
松弛:dist[B] = dist[A]-X
由于 V A B 指“只知道A到B的距离(边权)至少为1”
从 V A B 得到的差分系统为
dist[A] >= dist[B] +1
等价于
dist[B] <= dist[A] -1
则 if(dist[B] > dist[A] -1)
松弛:dist[B] = dist[A] -1
注意:
- (1)建立
<=
的差分系统,就必须求最短路(Bellman-Ford算法)。而信息是否可靠,就是判断图中是否存在负权回路(也是利用 Bellman-Ford判断) - (2)由于最坏有10W组不等式,经实测用
scanf
输入比cin
要节省1500ms - (3)经实测用优化Bellman-Ford算法比普通Bellman-Ford算法快2000ms
AC 源码
解题方法一:差分约束+优化Bellman
/*差分约束+优化Bellman*/
//Memory Time
//2596K 485MS
#include <iostream>
using namespace std;
const int inf=1000000000;
class
{
public:
int s,e;
}edge[200001];
int N; //太空站数目
int M; //tips数
int dist[1001]; //源点到各点的距离
int w[200001]; //边权
int main(int i,int j)
{
while(cin>>N>>M)
{
memset(dist,0,sizeof(dist)); //初始化源点到各点的距离
int pe=0;
for(i=0;i<M;i++)
{
char pv;
int a,b,x;
getchar(); //吃掉回车
scanf("%c",&pv); //由于要频繁输入,用scanf比cin要快1500ms
if(pv=='P') //清晰边权,即A、B间距离确定,建立双向边
{
scanf("%d%d%d",&a,&b,&x);
edge[pe].s=a;
edge[pe].e=b;
w[pe++]=x;
edge[pe].s=b;
edge[pe].e=a;
w[pe++]=-x;
}
else if(pv=='V') //模糊边权,即A、B间距离不确定,建立单向边
{
scanf("%d%d",&a,&b);
edge[pe].s=a;
edge[pe].e=b;
w[pe++]=1;
}
}
/*Bellman-Ford*/
bool sign; //用于Bellman-Ford算法优化
for(j=0;j<N;j++)
{
sign=false;
for(i=0;i<pe;i++)
if(dist[ edge[i].e ] > dist[ edge[i].s ] - w[i])
{
dist[ edge[i].e ] = dist[ edge[i].s ] - w[i];
sign=true;
}
if(!sign)//若dist没有任何改变,则以后也不会改变,可以直接退出循环
break;
}//循环n次后若flag == false 说明有负权回路,或者权值矛盾
if(sign)
cout<<"Unreliable"<<endl; //存在负权环
else
cout<<"Reliable"<<endl; //不存在负权环
}
return 0;
}
解题方法二:差分约束+无优化Bellman
/*差分约束+无优化Bellman*/
//Memory Time
//2596K 2438MS
#include<iostream>
using namespace std;
const int inf=1000000000;
class
{
public:
int s,e;
}edge[200001];
int N; //太空站数目
int M; //tips数
int dist[1001]; //源点到各点的距离
int w[200001]; //边权
int main(int i,int j)
{
while(cin>>N>>M)
{
memset(dist,0,sizeof(dist)); //初始化源点到各点的距离
int pe=0;
for(i=0;i<M;i++)
{
char pv;
int a,b,x;
getchar(); //吃掉回车
scanf("%c",&pv);
if(pv=='P') //清晰边权,即A、B间距离确定,建立双向边
{
scanf("%d%d%d",&a,&b,&x);
edge[pe].s=a;
edge[pe].e=b;
w[pe++]=x;
edge[pe].s=b;
edge[pe].e=a;
w[pe++]=-x;
}
else if(pv=='V') //模糊边权,即A、B间距离不确定,建立单向边
{
scanf("%d%d",&a,&b);
edge[pe].s=a;
edge[pe].e=b;
w[pe++]=1;
}
}
/*Bellman-Ford*/
/*Relax*/
for(j=0;j<N;j++)
for(i=0;i<pe;i++)
if(dist[ edge[i].e ] > dist[ edge[i].s ] - w[i])
dist[ edge[i].e ] = dist[ edge[i].s ] - w[i];
/*Judge the Negative Circle*/
bool sign=false;
for(i=0;i<pe;i++)
if(dist[ edge[i].e ] > dist[ edge[i].s ] - w[i])
{
sign=true;
break;
}
if(sign)
cout<<"Unreliable"<<endl; //存在负权环
else
cout<<"Reliable"<<endl; //不存在负权环
}
return 0;
}