差分约束板子
差分约束:把 i - j >= x 操作等价于从 j 到 i 连一条边权为 x 的边。然后跑一遍spfa统计最短路,即为一组合法序列。
这道题里要求以一个点为基准点 0 ,在跑完spfa之后统计最小的dis值,然后按顺序输出dis值减去这个最小值即可。
另一个问题是图不一定连通,有两个解决方法:
1.建一个万能点与所有的点相连,当然边权都是0.
2.跑spfa的时候要记录每个点的弹出次数,因此遍历一遍点,弹出次数为0就spfa,否则continue
代码:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,h[2005]={},dis[2005]={},cnt=0,minn,que[1000500]={},times[2005]={},head,tail;
bool vis[2005]={};
struct Edge
{
int to,next,w;
}e[10005];
void add(int u,int v,int w)
{
e[++cnt]=(Edge){v,h[u],w};
h[u]=cnt;
}
void spfa(int st)
{
head=0;
tail=1;
dis[st]=0;
que[1]=st;
times[st]++;
while(head<tail)
{
int u=que[++head];
vis[u]=0;
for(int i=h[u];i+1;i=e[i].next)
{
int v=e[i].to;
if(dis[v]>dis[u]+e[i].w)
{
dis[v]=dis[u]+e[i].w;
if(!vis[v])
{
vis[v]=1;
times[v]++;
if(times[u]>=n)
{
printf("NO SOLUTION\n");
exit(0);
}
que[++tail]=v;
}
}
}
}
vis[st]=0;
}
int main()
{
memset(h,-1,sizeof(h));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(v,u,w);
}
for(int i=1;i<=n;i++)
dis[i]=214748364;
for(int i=1;i<=n;i++)
{
if(times[i])
continue;
spfa(i);
}
minn=999999999;
for(int i=1;i<=n;i++)
minn=min(minn,dis[i]);
for(int i=1;i<=n;i++)
printf("%d\n",dis[i]-minn);
return 0;
}