差分约束系统
Part 1:知识点
问题引入
差分约束系统是一种特殊的
问题求解
每个约束条件
为了防止图不连通,可以增加一个超级源点
设
若约束条件形如
模板
#include<bits/stdc++.h>
using namespace std;
const int N=5010,M=5010;
int n,m,d[N],cnt[N];
int head[N],ver[2*M],nxt[2*M],edge[2*M],tot;
bool v[N];
queue <int> q;
void add(int x,int y,int z)
{
ver[++tot]=y; edge[tot]=z;
nxt[tot]=head[x]; head[x]=tot;
}
bool spfa()
{
memset(d,0x3f,sizeof(d));
d[0]=0; v[0]=1;
cnt[0]=-1;
q.push(0);
while(q.size())
{
int x=q.front(); q.pop();
v[x]=0;
for(int i=head[x]; i; i=nxt[i])
{
int y=ver[i],z=edge[i];
if(d[y]>d[x]+z)
{
d[y]=d[x]+z;
cnt[y]=cnt[x]+1;
if(!v[y])
q.push(y),v[y]=1;
}
if(cnt[y]>=n)
return 0;
}
}
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(y,x,z);
}
for(int i=1; i<=n; i++)
add(0,i,0);
if(!spfa())
printf("NO");
else
{
for(int i=1; i<=n; i++)
printf("%d ",d[i]);
}
return 0;
}
Part 2:练习题
Intervals
(题目传送门)
题目大意
有
解题思路
-
设
表示 到 取多少整数,那么对于每个区间 ,可构造不等式 。所以从 连一条长度为 的有向边 -
又由定义知,
,所以从 连一条长度为 的有向边,从 连一条长度为 的有向边 -
注意到
可能取到 ,所以整体右移。最后跑最长路即可
代码
#include<bits/stdc++.h>
using namespace std;
const int N=50010,M=50010*3;
int T,n,m,a,b,c,d[N];
int head[N],ver[M],nxt[M],edge[M],tot;
bool v[N];
queue <int> q;
void add(int x,int y,int z)
{
ver[++tot]=y; edge[tot]=z;
nxt[tot]=head[x]; head[x]=tot;
}
void spfa()
{
while(!q.empty())
q.pop();
memset(d,~0x3f,sizeof(d));
d[0]=0; v[0]=1;
q.push(0);
while(q.size())
{
int x=q.front(); q.pop();
v[x]=0;
for(int i=head[x]; i; i=nxt[i])
{
int y=ver[i],z=edge[i];
if(d[y]<d[x]+z)
{
d[y]=d[x]+z;
if(!v[y])
q.push(y),v[y]=1;
}
}
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
memset(head,0,sizeof(head));
tot=0;
n=0;
scanf("%d",&m);
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b+1,c);
n=max(n,b);
}
for(int i=0; i<=n; i++)
add(i+1,i,-1),add(i,i+1,0);
spfa();
printf("%d\n",d[n+1]);
if(T!=0)
printf("\n");
}
return 0;
}
赛车游戏
题目大意
R 君和小伙伴打算一起玩赛车。但他们被老司机 mocania 骗去了秋名山。
秋名山上有
可 mocania 的数学不大好,不知道怎么给边标长度,只能跑来请教你这个 OI 高手了。
解题思路
-
设
表示 到 的距离,又因为 ,所以我们将边权的讨论转化成 的讨论,这样只要求出一组 ,我们就可以算出每条边的长度,还保证了每一条 到 的路径都是等长的 -
因为
,所以 ,因此差分约束即可 -
但注意到,有些边并不在
到 的路径上,因此要先进行一次 ,只将有用的边添加到新图中。最后输出时没用的边输出 即可
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1010,M=2010;
int n,m,d[N],cnt[N];
int head[N],from[M],ver[M],nxt[M],tot;
bool v[N],vis[N],ab[N]; //ab[x]表示x能否到达n
vector < pair<int,int> > g[N];
queue <int> q;
void add(int x,int y)
{
ver[++tot]=y; from[tot]=x;
nxt[tot]=head[x]; head[x]=tot;
}
void add_edge(int x,int y) //差分约束建边
{
g[x].push_back(make_pair(y,9));
g[y].push_back(make_pair(x,-1));
}
bool dfs(int x)
{
if(x==n || ab[x])
return 1;
vis[x]=1;
for(int i=head[x]; i; i=nxt[i])
{
int y=ver[i];
if(vis[y])
{
if(ab[y])
add_edge(x,y),ab[x]=1;
continue;
}
if(dfs(y))
add_edge(x,y),ab[x]=1;
}
return ab[x];
}
bool spfa()
{
memset(d,0x3f,sizeof(d));
d[0]=0; v[0]=1;
cnt[0]=-1;
q.push(0);
while(q.size())
{
int x=q.front(); q.pop();
v[x]=0;
for(int i=0; i<g[x].size(); i++)
{
int y=g[x][i].first,z=g[x][i].second;
if(d[y]>d[x]+z)
{
d[y]=d[x]+z;
cnt[y]=cnt[x]+1;
if(!v[y])
q.push(y),v[y]=1;
}
if(cnt[y]>=n)
return 0;
}
}
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
}
if(!dfs(1)) //1无法到n
{
printf("-1");
return 0;
}
for(int i=1; i<=n; i++)
g[0].push_back(make_pair(i,0));
if(!spfa())
printf("-1");
else
{
printf("%d %d\n",n,m);
for(int i=1; i<=tot; i++)
{
int x=from[i],y=ver[i];
int z=d[y]-d[x];
printf("%d %d %d\n",x,y,(z<1 || z>9)? 1:z);
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探