差分约束POJ1201NIntervals解题报告

Intervals
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 23549 Accepted: 8897
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,
writes 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
Source
Southwestern Europe 2002
题意:第一行输入n,下面输入n个限制条件,条件的格式为 ai bi ci, 0<=ai<=bi<=50000,1<=ci<=bi-ai+1,表示在线段[ai,bi]上至少选出ci个点,使被选 出的点在集合Set里面。求Set的最小容量。
问题转化为求最短路径。难点是转化的过程。区间起点中最小的MIN是最短路起点,终点中最大的MAX是最短路的终点。
区间[ai,bi]表示点ai到点bi的一条边,ci表示该边权值的最小值。把图中的结点从1到N编号,添加边就是 add(ai, bi, ci),发现错误。因为假设两个限制条件分别为:1 3 2, 3 6 2; 那么按照上面的思路添加这两条边<3,1>=-2, <6,3>=-2; 后又可以得到边 <6,1>=-4, 意思是在线段[1,6]上至少要选4个点,而实际上不是的,应该是至少要选3个点才对,因为线段[1,3]和[3,6]有一个公共点3,当选到点3的时候,一共选3个点就能满足。所以要做调整:用左闭右开区间[ai, bi+1)代替[ai,bi]来表示边ai到bi,因为取的是整数点,所以意义和闭区间[ai,bi]一样,这样,对于每个限制条件(ai,bi,ci)就可以添加边< ai, bi+1>=ci;即 add(a,b+1,c)。用dis[x]表示源点MIN到点x-1的距离,边ai到bi长度为dis[bi+1]-dis[ai]。
仅仅依靠题中给出的边,不能构造一个连通图。例如题目的例子,点6和点7,不能相连。因此,我们需要将每个最小的区间[i,i+1)相连为边,但是权值为多少?找权值,就要考虑到dis[]数组,因为根据定义,dis[i+1]-dis[i]就是边[i,i+1)的权值,因为有0<=dis[i+1]-dis[i]<=1,即0<=dis[i+1]-dis[i]和dis[i]-dis[i+1]>=-1(因为要统一写成>=符号,所以会出现-1),因此需要加边add(i+1, i, 0);和add(i, i+1, -1)。这样,就构成了一个连通图。

//  6616K   297MS
//一直RE,数组干脆开到50万
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#include<cstdio>
#define MAXEDGE 500005
#define MAXVERTEX 500005
#define INF 1000000000
using namespace std;
int n,MIN,MAX,N;
int vis[MAXVERTEX];
int dis[MAXVERTEX];
int AdjEdge[MAXVERTEX];
struct Edge{
    int endV,priEdge,w;
}edge[MAXEDGE];
void addEdge(int startV,int endV,int w){
    edge[N].endV=endV;
    edge[N].w=w;
    edge[N].priEdge=AdjEdge[startV];
    AdjEdge[startV]=N++;
}
void init(){
    MIN=INF,MAX=0,N=1;
    memset(vis,0,sizeof(vis));
    memset(AdjEdge,0,sizeof(AdjEdge));
}
void spfa(){
    fill(dis+MIN,dis+MAX,-INF);//题目求的是最小的,所以-INF
    dis[MIN]=0;
    queue<int> q;
    q.push(MIN);
    while(!q.empty()){
        int frontV=q.front();q.pop();vis[frontV]=0;
        for(int i=AdjEdge[frontV];i!=0;i=edge[i].priEdge){
            if(dis[edge[i].endV]<dis[frontV]+edge[i].w){//题目求的是最小的,所以用<
                dis[edge[i].endV]=dis[frontV]+edge[i].w;
                if(!vis[edge[i].endV]){
                    vis[edge[i].endV]=1;
                    q.push(edge[i].endV);
                }
            }
        }
    }
    printf("%d\n",dis[MAX]);
}
int main()
{
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    while(scanf("%d",&n)!=EOF){
        int startV,endV,w;
        init();
        for(int i=1;i<=n;i++){
            scanf("%d%d%d",&startV,&endV,&w);
            addEdge(startV,endV+1,w);//由于经常调用addEdge函数,所以定义成inline函数,更快,时间是235ms。可见函数调用还是很耗时间的
            if(startV<MIN) MIN=startV;
            if(endV+1>MAX) MAX=endV+1;
        }
        for(int i=MIN;i<MAX;i++){
            addEdge(i,i+1,0);
            addEdge(i+1,i,-1);
        }
        spfa();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值