当前位置:网站首页>(部分不懂,笔记整理未完成)【图论】差分约束
(部分不懂,笔记整理未完成)【图论】差分约束
2022-08-02 06:01:00 【gzkeylucky】
知识点
一. 前置知识:判断负环
二.差分约束
差分约束系统即为n元一次不等式组,每个约束条件都是由两个变量作差构成的,形如,目标为求出一组解可以满足所有约束条件。
可变形为
,与最短路中三角形不等式
相似,于是将不等式组中的变量看作点,每个约束条件
为从节点 j 向节点 i 连一条边权为
的有向边,在跑完最短路后,
为差分约束系统中的一组解,若存在负环和终点不可达时,无解。
变形为
;
变形为
;
变形为
;
变形为
且
;
必要时,建一个超级源点。
那么,什么时候需要建立超级原点呢?
有的时候,为了保证整个区间的连通的,就需要建立一个超级源点使得整个图是连通的。但是需要注意的是,在正常建边的时候,如果是从大指向小,这个时候我们建立超级源点的时候就也应该遵循这个原则,如果是是从小指向大的,建立超级源点的时候反过来即可。
!求解最大解用最短路,求解最小解用最长路
题目描述
给出一组包含 m 个不等式,有 n 个未知数的形如:
的不等式组,求任意一组满足这个不等式组的解。
输入格式
第一行为两个正整数 n,m,代表未知数的数量和不等式的数量。
接下来 m 行,每行包含三个整数 c,c′,y,代表一个不等式 。
输出格式
一行,n 个数,表示 的一组可行解,如果有多组解,请输出任意一组,无解请输出
NO
。
输入输出样例
输入 #1
3 3 1 2 3 2 3 -2 1 3 1
输出 #1
5 3 5
说明/提示
样例解释
一种可行的方法是。
数据范围
对于 100% 的数据,,
,
,
。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
int n,m;
const int maxn=1e7+5,INF=0x3f3f3f3f;
struct node{
int to,next,w;
}edge[maxn<<1];
int head[maxn],num=0,dis[maxn],ans[maxn];
bool vis[maxn];
queue <int> q;
inline int read()
{
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
x=(x<<3)+(x<<1)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
inline void add(int u,int v,int w)
{
edge[++num].to=v;
edge[num].w=w;
edge[num].next=head[u];
head[u]=num;
}
inline bool spfa()
{
memset(dis,INF,sizeof(dis));
memset(ans,0,sizeof(ans));
memset(vis,0,sizeof(vis));
q.push(0); //从超级原点开始
vis[0]=1;
dis[0]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].w)
{
dis[v]=dis[u]+edge[i].w;
ans[v]=ans[u]+1;
if(ans[v] >= n+1)
{
return 1;
}
if(!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
return 0;
}
int main()
{
n=read(); m=read();
memset(head,-1,sizeof(head));
for(int i=1;i<=m;++i)
{
int a=read(),b=read(),y=read();
add(b,a,y); //要注意从b到a,减数放前面
}
for(int i=1;i<=n;++i)
{
add(0,i,0); //构造超级原点
}
if(spfa() == 1) printf("NO\n");
else
{
for(int i=1;i<=n;++i)
{
write(dis[i]);
putchar(' ');
}
}
return 0;
}
相关练习
思路:根据题目中的“至多”,“至少”和“一样多”,可以得知以下三条式子:
一般在解决问题的时候,我们希望能将问题转换为一个模型,或者用一个通式来解决大量的问题。
那么在上面三个式子中,①式和②式都为不等式,可以看作是图的有向边。我们希望把两个式子转换成相同的表达方式。那么将②式两边都乘以-1,将式子转换为 ,与①式相似。
但是,③式为等式,那么可以看作是图的无向边,除了③式,还可以表示为 ,在代码实现的时候,就像建立无向边一样建立两次。
为了保证图的联通,我们需要建一个保证联通的超级原点,再从这个超级原点开始跑最短路,最后,注意一定要判断负环!
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
int n,m;
const int maxn=1e7+5,INF=0x3f3f3f3f;
struct node{
int to,next,w;
}edge[maxn<<1];
int head[maxn],num=0,dis[maxn],ans[maxn];
bool vis[maxn];
queue <int> q;
inline int read()
{
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
x=(x<<3)+(x<<1)+(c^48);
c=getchar();
}
return x*f;
}
inline void add(int u,int v,int w)
{
edge[++num].to=v;
edge[num].w=w;
edge[num].next=head[u];
head[u]=num;
}
inline bool spfa()
{
memset(dis,INF,sizeof(dis));
memset(ans,0,sizeof(ans));
memset(vis,0,sizeof(vis));
q.push(0);
vis[0]=1;
dis[0]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].w)
{
dis[v]=dis[u]+edge[i].w;
ans[v]=ans[u]+1;
if(!vis[v])
{
q.push(v);
vis[v]=1;
}
if(ans[v]==n)
{
return 0;
}
}
}
}
return 1;
}
int main()
{
n=read(); m=read();
memset(head,-1,sizeof(head));
for(int i=1;i<=n;++i)
{
add(0,i,0);
}
for(int i=1;i<=m;++i)
{
int num=read();
int a,b,c;
if(num == 1) //a-b<=c的情况
{
a=read(),b=read(),c=read();
add(a,b,-c);
}
else if(num == 2) //a-b >= c的情况
{
a=read(),b=read(),c=read();
add(b,a,c);
}
else
{
a=read(),b=read();
add(a,b,0);
add(b,a,0);
}
}
if(spfa()== 1) printf("Yes");
else printf("No");
return 0;
}
2. 洛谷 P1250 种树
方法一:贪心算法
思路:这道题用贪心算法很好理解。为了保证所种的树最少,那么就需要在区间重合的部分种的树越多,然而重叠部分一定在区间的尾部,所以先对区间结束的节点进行排序,然后先依次在区间的头部从前往后种树直到满足要求,对于下一个区间,看看差多少树,就在结尾补多少。
步骤如下:
1. 按结束位置排序
2. 对每个区间进行处理:从前往后扫描区间,统计已有的树的个数
若已选点超过要求个数,则continue;否则从后往前,添加缺少的覆盖点
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
int n,h,ans=0;
const int maxn=1e7+5;
bool vis[maxn];
struct node{
int b,e,t;
}edge[maxn<<1];
inline int read()
{
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while(c>='0' && c<='9')
{
x=(x<<3)+(x<<1)+(c^48);
c=getchar();
}
return x*f;
}
inline bool cmp(node x,node y) //根据尾部节点进行排序
{
return x.e<y.e;
}
int main()
{
n=read(); h=read();
for(int i=1;i<=h;++i)
{
edge[i].b=read();
edge[i].e=read();
edge[i].t=read();
}
sort(edge+1,edge+h+1,cmp);
memset(vis,0,sizeof(vis));
for(int i=1;i<=h;++i)
{
int sum=0;
for(int j=edge[i].b;j<=edge[i].e;++j) //从前往后搜索区间
{
if(vis[j]!=0) //区间中存在被标记过的点
{
sum++;
}
}
if(sum >= edge[i].t) continue;
for(int j=edge[i].e;j>=edge[i].b;--j) //从后往前搜索
{
if(vis[j] == 0) //重叠部分增加要种的树
{
vis[j]=1;
sum++; //统计区间的数目增加
ans++; //总数增加
if(sum == edge[i].t) break;
}
}
}
printf("%d",ans);
return 0;
}
方法二:差分约束
思路:
根据题意:对于每个约束条件,最小值为w ,区间的长度可以表示为
,转换成两数之差为
,即
,表示在区间
之间至少有w棵树(sum[x]为x的前缀和)。又因为在每个间隔中只能选择不种或者是种1棵树,得出隐含条件:
。
在这道题中还需要注意的一点是:所建立的超级原点为n+1。根据题意,序号为1的位置是可以种树的,如果从0开始建立超级原点,可能会导致序号1的位置只能维持不种树的情况。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
int n,h;
const int maxn=1e7+5,INF=0x3f3f3f3f;
struct node
{
int to,next,w;
}edge[maxn<<1];
int head[maxn],num=0,dis[maxn];
struct node1
{
int b,e,t;
}edge1[maxn<<1];
bool vis[maxn];
queue <int> q;
inline int read()
{
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
x=(x<<3)+(x<<1)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
inline void add(int u,int v,int w)
{
edge[++num].to=v;
edge[num].w=w;
edge[num].next=head[u];
head[u]=num;
}
inline bool spfa()
{
memset(dis,INF,sizeof(dis));
q.push(n+1); //从超级原点开始查找
vis[n+1]=1;
dis[n+1]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].w)
{
dis[v]=dis[u]+edge[i].w;
if(!vis[v])
{
q.push(v);
vis[v]=1;
}
}
}
}
return 0;
}
int main()
{
n=read(); h=read();
memset(head,-1,sizeof(head));
for(int i=0;i<=n;++i)
{
add(n+1,i,0); //这里的超级原点是n+1
}
for(int i=1;i<=h;++i)
{
edge1[i].b=read();
edge1[i].e=read();
edge1[i].t=read();
add(edge1[i].e,edge1[i].b-1,-edge1[i].t); //建边方式
}
for(int i=1;i<=n;++i)
{
add(i,i-1,0);
add(i-1,i,1);
}
spfa();
write(-dis[0]); //输入的时候是负边权,输出要取反;
return 0;
}
边栏推荐
- APT + Transform to realize multi module Application distributed Application life cycle
- Nodejs installation and global configuration (super detailed)
- NPM ---- 安装yarn
- love
- HCIP BGP综合实验 建立对等体、路由反射器、联邦、路由宣告及聚合
- odoo field 设置匿名函数domain
- npm、cnpm的安装
- Node的安装与环境变量的配置
- 享年94岁,图灵奖得主、计算复杂性理论先驱Juris Hartmanis逝世
- C# Coding Conventions Handbook
猜你喜欢
APP专项测试:流量测试
MySQL高级-MVCC(超详细整理)
Node installation and environment configuration
MySQL(3)
MySQL high-level statements (1)
node安装及环境配置
Nacos installation detailed process
npm does not recognize the "npm" item as the name of a cmdlet, function, script file, or runnable program.Please check the spelling of the name, and if the path is included, make sure the path is corr
Toolbox App 1.25 新功能一览 | 版本更新
看图就懂|衡量业务增长健康的销售指标如何选择
随机推荐
HCIP BGP Comprehensive Experiment Establishing peers, route reflectors, federation, route announcement and aggregation
nacos源码启动找不到istio包
Servlet
引领需求 为HR价值正名——“人力资源领先模型HRLM”成功首发
项目开发规范
MySQL高级SQL语句
nodejs的安装和全局配置(超详细哦)
nacos安装配置和单机部署教程
ASP.NET Core Web API 幂等性
[Cartoon] 2021 full score programmer behavior comparison table (latest version)
Node installation and configuration of environment variables
Write implicit join on view with jOOQ 3.14 synthetic foreign key
mysql高阶语句(一)
MySQL 5.7 安装教程(全步骤、保姆级教程)
打卡day05
The stock price has repeatedly hit new lows, and the real estate SaaS giant is in trouble. How should Mingyuan Cloud transform and save itself?
Nacos installation configuration and single-machine deployment tutorial
Ue after video tutorial first
optional
MySQL high-level --- storage engine, index, lock