CSP-差分约束系统和最短路求解

CSP-差分约束系统及求解

知识简述

差分约束系统,是一种不等式系统,形式比较固定。具体的形式如下:
在这里插入图片描述在上面的例子中我们可以看到,差分约束系统的核心约束即为m个形如:xi-xj<=ck的不等式,解即为使所有条件都成立的一组答案。值得一提的是,差分约束系统一般说来都有无穷多个解,因此我们往往需要固定一个初始值来保证可以求出唯一解。
对于差分约束系统一般有两种形式:
1、xi-xj<=ck,求解的上限–>即xi-xj=ck的情况
2、xi-xj>=ck,求解的下限–>即xi-xj=ck的情况
对这两种情况,可以使用相同的转换思路,将xj移到不等式右侧后可以得到xi<=ck+xj求上限xi>=ck+xj求下限,熟悉最短路的松弛操作的同学就会发现,这个式子和松弛后得到的条件是一模一样的,也就是:将xi视为dis[i],将xj视为dis[j],ck视为边长,利用最短路算法来求解差分约束系统
进过转换后,具体的实现思路如下:
1、将每一个约束条件(例xi-xj<=ck)转换成一条有向边(j,i,ck),并存入图中
2、对生成好的图跑最短路算法(由于ck的值可能为负值,采用SPFA算法比较合理)
3、令dis[1]=0,得到的xi=dis[i]即为差分约束系统的一组解
两种特殊情况的处理:
1、出现负环,出现负环,说明某些点的dis[i]=-inf,则表达式变为xi-x1<=-Inf,我们无法找到这样的一个解,说明该差分约束系统无解。
2、出现某点不可达,说明某点满足dis[i]=inf,则表达式变为xi-x1<=Inf,该条件在任何取值下都满足,说明差分约束系统对xi无明确约束,可以取任何值。

题目概述

给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点

INPUT&输入样例

输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。
输入样例

5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1

OUTPUT&输出样例

输出一个整数表示最少选取的点的个数
输出样例:

6

题目重述

给定几个区间,每个区间内要求有n个点,求出能够满足条件的选择的最少点。

思路概述

由于题目要求最少点满足选点条件,看到这个题目的第一反应可能会想到是一道贪心算法的题目,且这道题和贪心问题里的区间选点问题十分相似。
但如果使用贪心算法,对多个点的贪心并不容易实现,可能需要比较复杂的模拟。这里介绍一种利用差分约束求解这道题的做法。
我们使用sum[i]来表示从源点0开始,选的点个数。(虽然题目中说明点的范围是1-5e4,但如果从sum[1]开始我们是无法衡量出1号点是否被选中,所以使用0号点开始操作比较方便)
条件A B a(A到B闭区间内选择a个点)可以转换为sum[B]-sum[A-1]>=a,通过类比发现该类型的条件与上述的差分约束系统十分相似,可以使用差分约束来巧妙求解。
但是只有这些边条件是不够的,因为实际意义,每个点只能是选择或者不选择两个可能,我们可以得出如下的条件0<=sum[i]-sum[i-1]<=1,对图中的每两点进行连接边即可。由于求的是最少点,所以需要将所有约束条件转换成xi-xj>=ck的形式。在转换完成后的图中跑一遍最长路即可得出解。
题目所求的最少点,也就是sum[maxi]的值。

题目源码(c++)

#include<iostream>
#include<stdio.h>
#include<queue>
using namespace std;


const int N=5e4+5;
struct Edge
{
    
    
    int des;
    int nxt;
    int value;
}Edges[N];

int edge_cnt;
int point_cnt;
int head[N];

void init()
{
    
    
    edge_cnt=0;
    for(int i=0;i<=N;i++)
    head[i]=-1;
}
void add(int x,int y,int value)
{
    
    
    edge_cnt++;
    Edges[edge_cnt].des=y;
    Edges[edge_cnt].nxt=head[x];
    Edges[edge_cnt].value=value;
    head[x]=edge_cnt;
}

int vis[N];
int dis[N];
queue<int> q;
void SPFA()
{
    
    
    while(!q.empty()) q.pop();
	for(int i=0;i<=point_cnt;i++)
    {
    
    
        vis[i]=0;
        dis[i]=0;
    }
    vis[0]=1,dis[0]=0;
    q.push(0);
    
    while(!q.empty())
    {
    
    
        int x=q.front();
        q.pop();
        vis[x]=0;
        for(int i=head[x];i>=0;i=Edges[i].nxt)
        {
    
    
            int y=Edges[i].des;
            if(dis[y]<dis[x]+Edges[i].value || (dis[y]==0&&dis[x]+Edges[i].value==0))
            {
    
    
                dis[y]=dis[x]+Edges[i].value;
                if(vis[y]==0)
                {
    
    
                    vis[y]=1;
                    q.push(y);
                }
            }
        }
    }
}
int main()
{
    
    
    int limit_number;
    cin>>limit_number;
    int start,end,value;
    init();
    int max_number=0;
    for(int i=0;i<limit_number;i++)
    {
    
    
        scanf("%d %d %d",&start,&end,&value);
        add(start,end+1,value);
        if(end+1>max_number) max_number=end+1;
    }
    point_cnt=max_number;
    for(int i=1;i<=point_cnt;i++)
    {
    
    
        add(i,i-1,-1);
        add(i-1,i,0);
    }
    
    SPFA();
    cout<<dis[point_cnt];
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43942251/article/details/105500167