题目大意
在一条坐标轴上有n个点,现在给出m组描述,每组描述是一以下两种形式之一:
- P A B X
V A B
第一种精确(Precise)表示点A在点B的右边X个位置,第二种模糊(Vague)只表示点A在点B右边
现在问你是否有满足这m组描述的情况。 (0<n≤1000,1≤m≤100000)
分析
比较明显的差分约束题,将上述描述用数学表达就是
形式1:
A−B=X
形式2:
A−B≥1
而形式1又可以转化成:
{A−B≥XB−A≥−X
这样就转化成了差分约束的标准形式问题是是否有满足m组描述的情况也就是问差分约束是否有解。
有解就必然存在最小解,是否有解就等价与是否无负环。
点数最多为n=1000,边数最多为2m=200000,所以bellman-ford很可能超时,用Spfa来求解。
一开始的做法是从每个点都出发跑一遍Spfa看看是否都无负环,但超时,后面看差分约束的相关讲解发现可以通过引入一个源点,源点到所有顶点的边权是0,这样就解决了从原图中一个点出发不能走完所有点的问题了。
spfa的bfs写法判断负环的方法是如果某个点的总入队次数大于总顶点数则说明存在负环。
代码
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
using namespace std;
const int INF=0x7fffffff;
struct Edge
{
int to;
int next;
int w;
}edge[201005];
int n,m;
int edgecount;
int flag;//flag=0表示无环
int head[1005];
int dis[1005];
int vis[1005];
int in[1005];//表示某个点进队的次数
void Init()
{
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
memset(in,0,sizeof(in));
edgecount=0;
flag=0;
}
void Add_edge(int u,int v,int w)
{
edge[++edgecount].to=v;
edge[edgecount].w=w;
edge[edgecount].next=head[u];
head[u]=edgecount;
}
void Spfa(int s)
{
for(int i=0;i<=n;i++)dis[i]=INF;
memset(vis,0,sizeof(vis));
memset(in,0,sizeof(in));
dis[s]=0;
vis[s]=1;//vis为1表示在队列里面
//if(s==4)cout<<5<<" "<<in[5]<<endl;
queue<int>Q;
Q.push(s);
while(!Q.empty())
{
int u=Q.front();
Q.pop();
vis[u]=0;
for(int k=head[u];k!=-1;k=edge[k].next)
{
int t=edge[k].w+dis[u];
if(dis[edge[k].to]>t)
{
dis[edge[k].to]=t;
if(in[edge[k].to]>n){flag=1;return ;}
if(vis[edge[k].to]==1)continue;
Q.push(edge[k].to);
vis[edge[k].to]=1;
in[edge[k].to]++;
}
}
}
}
int main()
{
char c;
int a,b,x;
while(scanf("%d%d",&n,&m)!=EOF)
{
Init();
for(int i=1;i<=m;i++)
{
scanf("%c",&c);
while(c==' ' || c=='\n')scanf("%c",&c);
if(c=='P')
{
scanf("%d%d%d",&a,&b,&x);
Add_edge(a,b,-x);
Add_edge(b,a,x);
}
else if(c=='V')
{
scanf("%d%d",&a,&b);
Add_edge(a,b,-1);
}
}
for(int i=1;i<=n;i++)
{
Add_edge(0,i,0);
}
Spfa(0);
if(flag==1)cout<<"Unreliable"<<endl;
else cout<<"Reliable"<<endl;
}
return 0;
}