差分约束小记
差分约束
形如
个 其中 是一组常数
差分约束里的细节:
- 超级源点基本都要建,注意
是否被用,是否能当源点。 - 一些有实际意义的(如距离,个数)都隐含着
的条件,注意隐含条件
例题难度基本递增
例题:
I P1260 工程规划
裸题:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3+10,M = 2e5+10;
//差分约束
int n,m;
struct made{
int ver,nx,ed;
}e[M<<1];
int hd[N],tot;
void add(int x,int y,int z){
tot++;
e[tot].nx = hd[x],e[tot].ver = y,e[tot].ed = z,hd[x] = tot;
}
int d[N],in[N];
bool v[N];
queue<int>q;
bool spfa(){
memset(d,0x3f,sizeof(d));
q.push(0),d[0] = 0,v[0] = 1;
while(!q.empty()){
int x = q.front();q.pop();
v[x] = 0;
for(int i = hd[x];i;i = e[i].nx){
int y = e[i].ver,z = e[i].ed;
if(d[x] + z < d[y]){
d[y] = d[x] + z;
in[y]++;
if(in[y] >= n)return 1;//有负环
if(!v[y])v[y] = 1,q.push(y);
}
}
}
return 0;
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++)add(0,i,0);//源点
for(int i = 1;i <= m;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(y,x,z);
}
if(spfa()){
printf("NO SOLUTION");
return 0;
}
int s = INT_MAX;
for(int i = 1;i <= n;i++)s = min(s,d[i]);//
for(int i = 1;i <= n;i++)d[i] -= s;
for(int i = 1;i <= n;i++)printf("%d\n",d[i]);
return 0;
}
II 362. 区间/SP116.Intervals
我们设 然后这道题就做完了
观察观察,本题中我们设的
,即 向 连一条长度为 的边。 ,即 向 连一条长度为 的边。
最后注意到我们把所有式子转化为了大于号,所以我们要
点击查看代码
#include <bits/stdc++.h>
using namespace std;
//差分约束
const int N = 5e4+10,M = 2e5+10;
int n;
struct made{
int ver,nx,ed;
}e[M];
int hd[N],tot;
void add(int x,int y,int z){
tot++;
e[tot].nx = hd[x],e[tot].ver = y,e[tot].ed = z,hd[x] = tot;
}
int d[N];
bool v[N];
void spfa(){
memset(d,-0x3f,sizeof(d));
queue<int>q;
q.push(0);d[0] = 0,v[0] = 1;
while(!q.empty()){
int x = q.front();q.pop();
v[x] = 0;
for(int i = hd[x];i;i = e[i].nx){
int y = e[i].ver,z = e[i].ed;
if(d[y] < d[x] + z){
d[y] = d[x] + z;
if(!v[y])q.push(y),v[y] = 1;
}
}
}
}
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
x++,y++;//从0开始全加1
add(x-1,y,z);
}
for(int i = 1;i <= 50001;i++){
add(i,i-1,-1);
add(i-1,i,0);
}
spfa();
printf("%d\n",d[50001]);//答案为50001
return 0;
}
III P2294 [HNOI2005] 狡猾的商人
本题较为简单,主要是细节
我们设
即
细节:因为我们
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 110,M = 2e3+10;
//差分约束
int t,n,m;
struct made{
int ver,nx,ed;
}e[M<<1];
int hd[N],tot;
void add(int x,int y,int z) {
tot++;
e[tot].nx = hd[x],e[tot].ver = y,e[tot].ed = z,hd[x] = tot;
}
int d[N],in[N];
bool v[N];
bool spfa(){
memset(in,0,sizeof(in));//
memset(v,0,sizeof(v));
memset(d,0x3f,sizeof(d));
queue<int>q;
q.push(n+1),d[n+1] = 0,v[n+1] = 1;
while(!q.empty()){
int x = q.front();q.pop();
v[x] = 0;
for(int i = hd[x];i;i = e[i].nx){
int y = e[i].ver,z = e[i].ed;
if(d[y] > d[x] + z){
d[y] = d[x] + z;
in[y]++;
if(in[y] > n)return 1;
if(!v[y])v[y] = 1,q.push(y);
}
}
}
return 0;
}
int main(){
freopen("bzoj_1202.in","r",stdin);
freopen("bzoj_1202.out","w",stdout);
scanf("%d",&t) ;
while(t--){
tot = 0;
memset(hd,0,sizeof(hd));
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;i++){
int s,t,v;
scanf("%d%d%d",&s,&t,&v);
add(s-1,t,v);
add(t,s-1,-v);
}
for(int i = 1;i <= n;i++)add(n+1,i,0);//源点不能为0
if(spfa())printf("false\n");
else printf("true\n");
}
return 0;
}
IV P3275 [SCOI2011] 糖果
看到这种
首先我们要求的是最小糖果数,要把式子变为
并且 ,即 向 连一条长度为 的边, 也向 连一条长度为 的边。 ,即 向 连一条长度为 的边。 ,即 向 连一条长度为 的边。 ,即 向 连一条长度为 的边。 ,即 向 连一条长度为 的边。- 因为每个小朋友都至少有一个糖果,满足不等式
,即 向 连一条长度为 的边。
让后直接跑SPFA
我们观察数据 SPFA已经死了。
我们建的图中的边权只有
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
//差分约束 + 强连通分量
const int N = 2e5+10,M = 4e5+10;
int n,m;
ll ans;
struct made{
int ver,nx,ed;
}e[M],E[M];
int hd[N],hde[N],tot,cnt,num,top,te;
int dfn[N],low[N],st[N],c[N],size[N],in[N];
bool v[N];
void add(int x,int y,int z){
tot++;
e[tot].nx = hd[x],e[tot].ver = y,e[tot].ed = z,hd[x] = tot;
}
void add_v(int x,int y,int z){
te++;
E[te].nx = hde[x],E[te].ver = y,E[te].ed = z,hde[x] = te;
}
void tarjan(int x){
low[x] = dfn[x] = ++cnt;
v[x] = 1,st[++top] = x;
for(int i = hd[x];i;i = e[i].nx){
int y = e[i].ver;
if(!dfn[y])tarjan(y),low[x] = min(low[x],low[y]);
else if(v[y])low[x] = min(low[x],dfn[y]);
}
if(dfn[x] == low[x]){
num++;int y = 0;
do{
y = st[top--];
v[y] = 0,c[y] = num;
size[num]++;
}while(x != y);
}
}//
int d[N];
void topsort(){
queue<int>q;
q.push(c[0]);d[0] = 0;
while(!q.empty()){
int x = q.front();q.pop();
ans += 1ll * size[x] * d[x];//累加答案
for(int i = hde[x];i;i = E[i].nx){
int y = E[i].ver,z = E[i].ed;
d[y] = max(d[y],d[x] + z);//最长路
if(--in[y] == 0)q.push(y);
}
}
}//拓扑排序
int main(){
freopen("tangguo.in","r",stdin);
freopen("tangguo.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;i++){
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
switch(op){
case 1:add(x,y,0),add(y,x,0);break;
case 2:add(x,y,1);break;
case 3:add(y,x,0);break;
case 4:add(y,x,1);break;
case 5:add(x,y,0);break;
}
}
for(int i = 1;i <= n;i++)add(0,i,1);//源点
for(int i = 0;i <= n;i++)
if(!dfn[i])tarjan(i);
for(int x = 0;x <= n;x++)
for(int i = hd[x];i;i = e[i].nx){
int y = e[i].ver,z = e[i].ed;
if(c[x] == c[y] && z){//一个连通分量中有边权为1的边无解
printf("-1\n");
return 0;
}
else if(c[x] != c[y])add_v(c[x],c[y],z),in[c[y]]++;
}
topsort();
printf("%lld\n",ans);
return 0;
}
V P4926 [1007] 倍杀测量者
写了这道题要女装
首先我们可以找出式子如果
变为
我们知道一些固定的值
注:本题精度要求不是很大,不需要太精确,所以
可以大致为 。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
//乘法差分约束
const double eps = 1e-6,inf = 1e8;
const int N = 1e3+10,M = 1e5+10;
int n,m,t;
struct made{
int nx,ver,op;
double ed;
}e[N<<1];
int hd[N],tot;
void add(int x,int y,double z,int op){
tot++;
e[tot].nx = hd[x],e[tot].ver = y,e[tot].ed = z,e[tot].op = op,hd[x] = tot;
}
double d[N];
int in[N];
bool v[N];
bool spfa(double mid){
memset(v,0,sizeof(v));
memset(in,0,sizeof(in));
for(int i = 1;i <= n;i++)d[i] = inf;
queue<int>q;
q.push(0),d[0] = 0,v[0] = 1;
while(!q.empty()){
int x = q.front();q.pop();
v[x] = 0;
for(int i = hd[x];i;i = e[i].nx){
int y = e[i].ver;
double z = e[i].ed;
if(e[i].op == 1)z = -log(z-mid);//第1种边
if(e[i].op == 2)z = log(z+mid);//第2种边
if(d[y] > d[x] + z){
d[y] = d[x] + z;
if(!v[y]){
v[y] = 1,in[y]++;
if(in[y] > n+1)return 1;
q.push(y);
}
}
}
}
return 0;
}
int main(){
scanf("%d%d%d",&n,&m,&t);
double l = 0,r = INT_MAX;
for(int i = 1;i <= m;i++){
int op,a,b;double k;
scanf("%d%d%d%lf",&op,&a,&b,&k);
add(a,b,k,op);
if(op == 1)r = min(r,k);//二分右界
}
for(int i = 1;i <= t;i++){
int x;double z;
scanf("%d%lf",&x,&z);
add(0,x,log(z),0),add(x,0,-log(z),0);
}
if(!spfa(0))printf("-1\n");//全部人都不需要女装,即不存在T
else{
while(l + eps < r){
double mid = (l + r) / 2;
if(spfa(mid))l = mid;
else r = mid;
}
printf("%lf\n",r);
}
return 0;
}
VI P5590 赛车游戏
第一眼看咋看都不像差分约束。
因为我们要找的是
- 我们假设
,因为 的路径是一定的,那么我们设 的路径长度为 ,则一定有 得到 ,与假设相悖,假设不成立。
得证
那么我们又知道每个边的边长在
但是其实那些不在
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
//差分约束
const int N = 1e3+10,M = N<<3;
int n,m;
struct made{
int ver,nx,ed;
}e[M],e1[M];
struct node{
int x,y;
}li[M];
int hd[N],hd1[N],tot;
void add(int x,int y,int z){
tot++;
e[tot].nx = hd[x],e[tot].ver = y,e[tot].ed = z,hd[x] = tot;
}
void add1(int x,int y){
tot++;
e1[tot].nx = hd1[x],e1[tot].ver = y,hd1[x] = tot;
}
int d[N],in[N];
bool v[M],vi[N];
bool spfa(){
memset(v,0,sizeof(v));
memset(d,0x3f,sizeof(d));
queue<int>q;
q.push(1),d[1] = 0,v[1] = 1;
while(!q.empty()){
int x = q.front();q.pop();
v[x] = 0;
for(int i = hd[x];i;i = e[i].nx){
int y = e[i].ver,z = e[i].ed;
if(d[y] > d[x] + z){
d[y] = d[x] + z;
if(!v[y]){
v[y] = 1,in[y]++;
if(in[y] > n)return 1;
q.push(y);
}
}
}
}
return 0;
}
bool dfs(int x){//1~n的路径
if(x == n || vi[x])return 1;
for(int i = hd1[x];i;i = e1[i].nx){
int y = e1[i].ver;
if(v[i])continue;//记录边
v[i] = 1;
if(dfs(y)){//可以到n
vi[x] = 1;
add(x,y,9),add(y,x,-1);
}
}
return vi[x];
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;i++){
int x,y;
scanf("%d%d",&x,&y);
add1(x,y);
li[i].x = x,li[i].y = y;
}
tot = 0;
if(!dfs(1))printf("-1\n");//到不了n
else if(spfa())printf("-1\n");
else{
printf("%d %d\n",n,m);
for(int i = 1;i <= m;i++){
int dis = d[li[i].y] - d[li[i].x];
dis = (dis < 10 && dis > 0)?dis:1;//其他边任意赋值即可
printf("%d %d %d\n",li[i].x,li[i].y,dis);
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)