POJ 1201 Intervals(差分约束系统,最长路正环判断)

本文探讨了差分约束系统在解决区间覆盖问题中的应用,通过建立差分约束模型,找出隐含关系,实现了对给定区间的最优覆盖方案。文章提供了详细的代码实现,包括如何处理负节点和避免负环,以及如何确保增量最多为一。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

                                                   Intervals

Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 32163 Accepted: 12444

Description

You are given n closed, integer intervals [ai, bi] and n integers c1, ..., cn. 
Write a program that: 
reads the number of intervals, their end points and

integers c1, ..., cn from the standard input,
computes the minimal size of a set Z of integers which has at least

ci common elements with interval [ai, bi], for each i=1,2,...,n, 

rites the answer to the standard output. 

Input

The first line of the input contains an integer n (1 <= n <= 50000) -- the number of intervals.

The following n lines describe the intervals. The (i+1)-th line of the input contains three integers ai,

bi and ci separated by single spaces and such that 0 <= ai <= bi <= 50000

and 1 <= ci <= bi - ai+1.

Output

The output contains exactly one integer equal to the minimal size of set Z sharing at

least ci elements with interval [ai, bi], for each i=1,2,...,n.

Sample Input

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

Sample Output

6

初次接触差分约束系统的题目,感觉 题解中 的难点有:

①建立差分约束模型(同时明确什么是点,什么是边权等)

②找出隐含关系;

先直接上代码吧:

#define ll long long
using namespace std;
const int N=50000+10;
const int M=50010*4;
int head[N];
int edge[M],ver[M];
int tot;
int v[N];
int Next[M];
int d[N];
void add(int x,int y,int z)
{
     edge[++tot]=z;
     ver[tot]=y;
     Next[tot]=head[x];
     head[x]=tot;
}
queue <int> q;
int cnt[M];
int n;
bool spfa(int minn,int maxx)
{
    memset(cnt,0,sizeof(cnt));
    for(int i=minn;i<=maxx;++i)d[i]=-999999999;
    memset(v,0,sizeof(v));
    d[minn]=0;//注意起点不再是“1”点
    v[minn]=1;
    cnt[minn]=0;
    q.push(minn);
    while(q.size())
    {
        int x=q.front();
        q.pop();
        v[x]=0;
        for(int i=head[x];i;i=Next[i])
        {
            int y=ver[i];
            int z=edge[i];
            if(d[y]<d[x]+z)//求最长路
            {
                d[y]=d[x]+z;
                cnt[y]=cnt[x]+1;
                if(cnt[y]>=maxx-minn+1)return 0;//负环判断
                if(!v[x])q.push(y),v[y]=1;
            }
        }
    }
    return 1;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        int minn=999999999;
        int maxx=-999999999;
        tot=0;
        memset(head,0,sizeof(head));
        memset(edge,0,sizeof(edge));
        int x,y,z;
        for(int i=1;i<=n;++i)
        {
            scanf("%d%d%d",&x,&y,&z);
            add(x,y+1,z);//只有单向边
            minn=min(minn,x);//所以,起点只从x中取得
            maxx=max(maxx,y+1);//终点只从y+1中取得
        }
        //下面这个for是题目隐含的不等关系,需要自己看出来;
        for(int i=minn;i<maxx;++i)
       //这里i小于或者小于等于maxx均可以,i+1点对原点到i的最长路我影响
        {
            add(i,i+1,0);
            add(i+1,i,-1);
        }
        bool f=spfa(minn,maxx);
        if(f)printf("%d\n",d[maxx]);
    }
    return 0;
}

第二次做:

POJ 1201:区间覆盖差分约束:

核心题意:

题意:有n个如下形式的条件:

ai bi ci,表示在区间[ai, bi]内至少要选择ci个整数点.

问你满足n个条件的情况下,最少需要选多少个点?

理解:

       不在题目描述区间中的点对最终结果没有影响,

       所有有负节点的可以加上一个较大数使得所有点均为正数。

注意:

      隐含条件,这种题目中,所求最长路最短路表示前缀和,所以后面的一定要>=前面的,

且保证增量最多为一。

      注意负环判断时的条件:n指的那个量。(应该是节点中的最大值,不区分是否在题目

描述的几个小区间内是否出现)。

      因为所有d值均大于等于0,所以只能求最长路,求最短路没法通过0节点将所有点相连接。

AC代码:(还是最长路)

#define LL long long
#define par pair<int,int>
#define INF 0x3f3f3f3f
#define io ios::sync_with_stdio(false)
using namespace std;
const int N=1e5+100;
const int M=2e5+100;
using namespace std;
int head[N];
int Next[M];
int edge[M];
int ver[M];
int tot;
void add(int x,int y,int z)
{
    ver[++tot]=y;
    Next[tot]=head[x];
    edge[tot]=z;
    head[x]=tot;
}
int d[N],vis[N],cnt[N];
int spfa(int n)
{
    queue<int>q;
    while(q.size())q.pop();
    memset(vis,0,sizeof(vis));
    memset(cnt,0,sizeof(cnt));
    memset(d,-INF,sizeof(d));
    d[1]=0;
    vis[1]=1;
    cnt[1]=0;
    q.push(1);
    while(q.size())
    {
        int x=q.front();
        q.pop();
        vis[x]=0;
        for(int i=head[x];i;i=Next[i])
        {
            int y=ver[i];
            if(d[y]<d[x]+edge[i])
            {
                d[y]=d[x]+edge[i];
                cnt[y]=cnt[x]+1;
                if(cnt[y]>=n)return 0;
                if(vis[y]==0)vis[y]=1,q.push(y);
            }
        }
    }
    return 1;
}
int main()
{
    io;
    cin.tie(0);
    cout.tie(0);
    int n;
    while(cin>>n)
    {
        memset(head,0,sizeof(head));
        memset(Next,0,sizeof(Next));
        memset(ver,0,sizeof(ver));
        memset(edge,0,sizeof(edge));
        tot=1;
        int maxx=-1;
        for(int i=1;i<=n;++i)
        {
            int x,y,z;
            cin>>x>>y>>z;
            x+=1;
            y+=1;
            if(x>maxx)maxx=x;
            if(y>maxx)maxx=y;
            add(x-1,y,z);
        }
        for(int i=2;i<=maxx;++i)add(1,i,0);
        for(int i=2;i<=maxx;++i)add(i+1,i,-1);
        for(int i=3;i<=maxx;++i)add(i-1,i,0);
        int f=spfa(maxx);
        if(f)
            cout<<d[maxx]<<endl;
    }
    return 0;
}

 

The end;

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值