POJ 1716 Integer Intervals(贪心or差分约束)

POJ 1716 Integer Intervals(贪心or差分约束)

An integer interval [a,b], a < b, is a set of all consecutive integers beginning with a and ending with b.
Write a program that: finds the minimal number of elements in a set containing at least two different integers from each interval.

Input

The first line of the input contains the number of intervals n, 1 <= n <= 10000. Each of the following n lines contains two integers a, b separated by a single space, 0 <= a < b <= 10000. They are the beginning and the end of an interval.

Output

Output the minimal number of elements in a set containing at least two different integers from each interval.

Sample Input

4
3 6
2 4
0 2
4 7

Sample Output

4

题意

有一段数,1~N,让你从这N个数中选择ans个。输入N个区间,要求满足每个区间至少包含2个被选择的数。输出最小ans。

思路

有两种做法
一、因为只要满足在给出的N个区间内都要选择两个数,所以可以尽量在两个区间有交集的部分选择数,比如【1,4】和【2,3】,只要选择2、3两个数,就能满足两个区间都有两个数。
所以可以对区间右端点排序,贪心即可。每次可以尽量选区间右端点的两个数,这样可以尽量使后面的区间也尽量包含已经选了的数。

二、差分约束
设a[i]为从起点到第i个点选择了多少个数
所以对于输入的区间【left,right】有a[right]-a[left-1]>=2
因为left>=0,为了方便处理负数,可以左右端点统一 +1,上式等价a[right+1]-a[left]>=2
但是只有N个区间给出的关系还是不能建图
所以还有以下隐含关系
a[i+1]-a[i]>=0
a[i+1]=a[i]<=1

之后就可以建图,跑一边最短路 spfa 即可
通过上面三个关系,可以如下方式具体建边(单向边)
right+1,left,-2
i,i+1,1
i+1,i,0

思路一
AC代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define maxn 10010
#define maxm 210
typedef struct node{
    int l,r;
}node;
node a[maxn];
bool cmp(node a,node b)  //按右端点排序
{
    return a.r<b.r;
}

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF){
        int i;
        for(i=0;i<n;i++){
            scanf("%d%d",&a[i].l,&a[i].r);
        }
        sort(a,a+n,cmp);
        int ans=2;  //答案  
        int x=a[0].r-1,y=a[0].r;  //起始选好了两个点
        for(i=1;i<n;i++){
            //如果已经选的点有两个点在当区间,则不需要继续选新点
            if(a[i].r>=y&&a[i].l<=x){  
                continue;
            }
            //如果已经选的点都在当前区间左端点之前,则需要选两个新点
            if(a[i].l>y){
                x=a[i].r-1;  //贪心可知,每次选区间右端点的两个点最优
                y=a[i].r;
                ans+=2;  
            }
            else{  //上面两个if外的情况就是只包含了一个点,所以此时再选择一个右端点即可
                x=y;
                y=a[i].r;
                ans++;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

思路二
AC代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define maxn 10010
#define maxm 210
int tot,ma,mi;
int head[maxn],dis[maxn];
bool vis[maxn];
typedef struct node{
    int v,nex,w;
}node;
node e[maxn+maxn+maxn];  //要开三倍的N,否则可能会RT
void add(int u,int v,int w)  //建边
{
    e[tot].v=v;
    e[tot].w=w;
    e[tot].nex=head[u];
    head[u]=tot++;
}
void spfa()
{
    //printf("ans:\n");
    for(int i=0;i<ma;i++){  //初始化
        dis[i]=inf;
        vis[i]=0;
    }
    dis[ma]=0;
    queue<int>q;
    q.push(ma);
    while(!q.empty()){
        int v,u=q.front();
        q.pop();
        vis[u]=false;
        for(int i=head[u];i!=-1;i=e[i].nex){
            v=e[i].v;
            if(dis[v]>dis[u]+e[i].w){
                dis[v]=dis[u]+e[i].w;
                if(!vis[v]){
                    q.push(v);
                    vis[v]=true;
                }
            }
        }
    }
    printf("%d\n",abs(dis[mi]-dis[ma]));
}
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF){
        tot=0;
        mi=inf;
        ma=-inf;
        memset(head,-1,sizeof(head));
        int i,l,r;
        for(i=0;i<n;i++){
            scanf("%d%d",&l,&r);
            mi=min(mi,l);
            ma=max(ma,r+1);
            add(r+1,l,-2);  
        }
        for(i=0;i<ma;i++){
            add(i,i+1,1);
            add(i+1,i,0);
        }
        spfa();
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值