POJ 1384 Intervals (区间差分约束,根据不等式建图,然后跑spfa)

传送门:

http://acm.hdu.edu.cn/showproblem.php?pid=1384

Intervals

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 4841    Accepted Submission(s): 1815


Problem 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 endpoints 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,

> writes the answer to the standard output
 
Input
The first line of the input contains an integer n (1 <= n <= 50 000) - 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 <= 50 000 and 1 <= ci <= bi - ai + 1.

Process to the end of file.

 
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
 
Author
1384
 
Recommend
Eddy   |   We have carefully selected several similar problems for you:  1529 1531 1548 1534 1317 
题目意思:
给出 n 个区间,每个区间有个权值 Ci,最终找出一个最少的数字的集合,使得满足每个区间中至少包含 Ci 个数。
给你几组的a,b,c
从区间a到b(闭区间)选择至少c个数放入集合
要求集合中的数字最少,问你最少多少个数字
 
分析:
f(a)表示从0到a有f(a)个数放入集合
那么a,b,c根据不等式建立边
f(b)-f(a-1)>=c
 
这个不等式的意思是:从区间a,b里面选择至少c个数加入集合
隐藏的不等式:0<=f(i)-f(i-1)<=1
 
变形一下:
f(i)-f(i-1)>=0
f(i-1)-f(i)>=-1
 
根据这三个不等式建立边
找到区间在最左端:minn
找到区间的最右端:maxx
所以这样建立边的话,跑最短路的时候
起点应该是max,终点是min-1
f(max)-f(min-1)>=x
x就是我们需要的结果
code:
#include <iostream>
#include <cstdio>
#include<stdio.h>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<memory>
#include<queue>
#include<vector>
using namespace std;
#define max_v 50010
#define INF 9999999
int tot;
int head[max_v];
int vis[max_v];
int dis[max_v];
int minn,maxx;
struct node
{
    int u,v,val;
    int next;
}Edge[max_v<<2];
void addEdge(int u,int v,int val)
{
    Edge[tot].u=u;
    Edge[tot].v=v;
    Edge[tot].val=val;
    Edge[tot].next=head[u];
    head[u]=tot++;
}
void spfa()
{
    for(int i=minn-1;i<=maxx;i++)
        dis[i]=-INF;
    queue<int> q;
    dis[maxx]=0;
    vis[maxx]=1;
    q.push(maxx);
    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].v;
            if(dis[v]<dis[u]+Edge[i].val)
            {
                dis[v]=dis[u]+Edge[i].val;
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    printf("%d
",dis[minn-1]);
    return ;
}
int main()
{
    int n,a,b,c;
    while(~scanf("%d",&n))
    {
        tot=0;
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        maxx=0;
        minn=INF;
        for(int i=0;i<n;i++)
        {
            scanf("%d %d %d",&a,&b,&c);
            a++;
            b++;
            minn=min(minn,a);
            maxx=max(maxx,b);
            addEdge(b,a-1,c);
        }
        for(int i=minn;i<=maxx;i++)
        {
            addEdge(i,i-1,0);
            addEdge(i-1,i,-1);
        }
        spfa();
    }
    return 0;
}

今天学差分约束的时候又遇到了这个题,跑最长路初始化的时候搞错了,应该是dis[i]=-INF。。。。,卡了好一会

题目意思:
从一系列区间[a,b]中每个至少取ci个数构成集合s,问你集合s至少需要多少个数
区间约束问题,差分约束解决
分析:
f(x):[0,x]区间内取f(x)个数
则区间[a,b]内至少取c个数可以变形为:f(b)-f(a-1)>=c
隐藏关系:1>=f(i)-f(i-1)>=0,变形一下:
f(i)-f(i-1)>=0
f(i-1)-f(i)>=-1
maxx:所有区间的最大值
minx:所有区间的最小值
i属于[minx,maxx]
总结一下得到这样三个关系:
f(b)-f(a-1)>=c
f(i)-f(i-1)>=0  i属于[minx,maxx]
f(i-1)-f(i)>=-1 i属于[minx,maxx]
这三个表达式的形式都是一样的,xi-xj>=c
按照j->i建边,权值为c
所以:
(a-1)->b 建边 权值c
(i-1)->i 建边 权值0
i->(i-1) 建边 权值-1
我们要求的东西ans可以变形为:f(maxx)-f(minx-1)>=ans
>=是最小值,所以是最长路
所以是问你从minx-1到maxx的最长路
建图完毕之后spfa跑一遍最长路,不能用dj,因为存在负权边
code:
#include<stdio.h>
#include<iostream>
#include<math.h>
#include<string.h>
#include<set>
#include<map>
#include<list>
#include<math.h>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long LL;
#define INF 9999999999
#define me(a,x) memset(a,x,sizeof(a))
int mon1[13]= {0,31,28,31,30,31,30,31,31,30,31,30,31};
int mon2[13]= {0,31,29,31,30,31,30,31,31,30,31,30,31};
int dir[4][2]= {{0,1},{0,-1},{1,0},{-1,0}};

int getval()
{
    int ret(0);
    char c;
    while((c=getchar())==' '||c=='
'||c=='
');
    ret=c-'0';
    while((c=getchar())!=' '&&c!='
'&&c!='
')
        ret=ret*10+c-'0';
    return ret;
}
void out(int a)
{
    if(a>9)
        out(a/10);
    putchar(a%10+'0');
}

#define max_v 50005
int dis[max_v];
int vis[max_v];

struct node
{
    int v,w;
    node(int vv=0,int ww=0):v(vv),w(ww){}
};
vector<node> G[max_v];
int n,m;

void init()
{
    for(int i=0;i<max_v;i++)
        G[i].clear();
    for(int i=0;i<max_v;i++)
    {
        dis[i]=-INF;
        vis[i]=0;
    }
}

void spfa(int s)
{

    queue<int> q;
    q.push(s);
    vis[s]=1;
    dis[s]=0;

    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;

        for(int j=0;j<G[u].size();j++)
        {
            int v=G[u][j].v;
            int w=G[u][j].w;
            if(dis[v]<dis[u]+w)//最长路
            {
                 dis[v]=dis[u]+w;
                 if(vis[v]==0)
                 {
                     vis[v]=1;
                     q.push(v);
                 }
            }
        }
    }
}

int f(int u,int v)
{
   for(int j=0;j<G[u].size();j++)
   {
       if(G[u][j].v==v)
        return 0;
   }
   return 1;
}
int main()
{
    int a;
    while(~scanf("%d",&a))
    {
        int x,y,w;
        init();
        int maxx=-INF,minx=INF;
        for(int i=1;i<=a;i++)
        {
            scanf("%d %d %d",&x,&y,&w);
            x++,y++;//防止减1的时候出现负数
            maxx=max(maxx,y);
            minx=min(minx,x);
            if(f(x-1,y))//预防重边
                G[x-1].push_back(node(y,w));
        }
        for(int i=minx;i<=maxx;i++)
        {
            if(f(i-1,i))//预防重边
                G[i-1].push_back(node(i,0));
            if(f(i,i-1))//预防重边
                G[i].push_back(node(i-1,-1));
        }
        spfa(minx-1);
        printf("%d
",dis[maxx]);
    }
    return 0;
}
/*
题目意思:
从一系列区间[a,b]中每个至少取ci个数构成集合s,问你集合s至少需要多少个数
区间约束问题,差分约束解决

分析:
f(x):[0,x]区间内取f(x)个数

则区间[a,b]内至少取c个数可以变形为:f(b)-f(a-1)>=c
隐藏关系:1>=f(i)-f(i-1)>=0,变形一下:
f(i)-f(i-1)>=0
f(i-1)-f(i)>=-1
maxx:所有区间的最大值
minx:所有区间的最小值

i属于[minx,maxx]

总结一下得到这样三个关系:
f(b)-f(a-1)>=c
f(i)-f(i-1)>=0  i属于[minx,maxx]
f(i-1)-f(i)>=-1 i属于[minx,maxx] 

这三个表达式的形式都是一样的,xi-xj>=c
按照j->i建边,权值为c
所以:
(a-1)->b 建边 权值c
(i-1)->i 建边 权值0
i->(i-1) 建边 权值-1

我们要求的东西ans可以变形为:f(maxx)-f(minx-1)>=ans
>=是最小值,所以是最长路
所以是问你从minx-1到maxx的最长路
建图完毕之后spfa跑一遍最长路,不能用dj,因为存在负权边

*/
原文地址:https://www.cnblogs.com/yinbiao/p/9445855.html