zoukankan      html  css  js  c++  java
  • [题解] poj 1716 Integer Intervals (差分约束+spfa)

    - 传送门 -

     http://poj.org/problem?id=1716

    #Integer Intervals | **Time Limit:** 1000MS |   | **Memory Limit:** 10000K | | **Total Submissions:** 15086 |   | **Accepted:** 6375 |

    Description

    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

    Source

    CEOI 1997


    [Submit]   [Status]   [Discuss]

    - 题意 -

     有 n 个区间.
     求一个集合, 使得每一个区间都至少有两个数在该集合中.
     问集合中最少有几个数.
     

    - 思路 -

     有思想的差分约束.
     差分约束的详解: http://www.cppblog.com/menjitianya/archive/2015/11/19/212292.html
     首先设 (S[x]) 表示 (0 o x) 中有 (S[x]) 个数.
     可知 :
     对于给定区间 [a, b] 有:
      (S[b] - S[a-1] >= 2)  --- 1
     对于全体有:
      (S[x] - S[x-1] >= 0)  --- 2
      (S[x] - S[x-1] <= 1)  --- 3
     
     先谈谈我对差分约束的理解:
     假设我们有一些关于 (D) 的不等式组, 要求(D[n] - D[0])的最大值
     若要以不等式来建边, 先要保证不等式组(默认左边是变量, 右边是常数,例如上面的式子)的符号一致. (默认0是起点, n 是终点)
     
     假设得到的式子形式如下:
     (D[y] - D[x] <= m) (保证 <= 号, y 大于 x)
     那我们就建一条(x) 指向 (y) 的权值为 (m) 的有向边.(谨记式子的表示: 指向的点 - 指出的点 = 边权)
     要求(max(D[n] - D[0])), 所以看成是取了等号, 使边权尽量大.
     
     假设找到了一条可行的路径:(0 o^{v1} a o^{v2} b o^{v3} n), 由上式可知:
     (D[n]-D[b]=v3)
     (D[b]-D[a]=v2)
     (D[a]-D[0]=v1)
     三式相加得:
     (D[n]-D[0]=v1+v2+v3)
     也就是说, 找到(0), (n) 两点间的路径, 也就找到了一个(D[n]-D[0]) 的值.
     
     但我们知道, 一开始给出的是不等式组, 找出的答案满足这条路径表示的不等式, 却不一定满足其它的, 所以我们要找的是最短路, 最短路找到的解是最小的, 对于固定小于等于号的不等式来说, 一定满足不在最短路上的路径表示的式子.
     (可以理解为, 对于非最短路, 我们求得的解能作为不等式左侧满足不等式, 那对于一个更小的解(最短路)当然也满足了)
     
     综上, (max(D[n]-D[0]))即为最短路.
     
     再看这道题, 它让我们求的是最小值, 那我们可以反过来, 已知(D[n]-D[0])一定是正数, 我们可以求负数(D[0]-D[n])的最大值, 大部分步骤和上面一样, 只是要从 (n) 找起, 找到 (0) 后再取相反数.
     因为我们的式子形式为(D[x] - D[y] <= m) (保证 <= 号, y 大于 x)(可以把最开始的三个式子中的 1 化成小于等于看看是不是这样), 据此, 我们会建从大点连向小点的边, 所以要从 (n) 找起.
     (对于 3 式来说确实是小点连向大点, 但是我们要求的是(D[0]-D[n]), 所以要保证选到的不等式加起来左边变成(D[0]-D[n]), 也就是 (n)(0) 的一条边).
     
     同样我们可以对不等式组固定大于等于号(左变量右常量)来解决问题.
     大致上和小于号的情况是一样的, 只是要保证找到的解满足每一条路径(大于等于号的不等式), 我们要求的就变成了最长路,其它的就照上面分析就好了.
     
     总的来说, 分析这种问题的关键在于分析每条边表示的不等式, 把路径上的不等式综合一下, 看左边(变量)得到的是什么.
     
     对于本题来说, 上面提到的 n 应该是区间右端点最大值, 还要注意为了赋初值, 我们把给出的数都看大 1 , S[0] 就默认为 0 了.
     
     细节见代码.
     

    - 代码 -

    大于号:

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    using namespace std;
    
    const int N = 2e4 + 5;
    const int M = 4e4 + 5;
    
    int HD[N], NXT[M], TO[M], V[M];
    int VIS[N], DIS[N];
    int n, m, r, sz;
    
    queue<int>Q;
    
    void add(int frm, int to, int val) {
    	V[++sz] = val; TO[sz] =to;
    	NXT[sz] = HD[frm]; HD[frm] = sz;
    }
    
    int spfa() {
    	memset(DIS, 128, sizeof (DIS));
    	DIS[0] = 0;
    	Q.push(0);
    	while (!Q.empty()) {
    		int u = Q.front();
    		Q.pop();
    		VIS[u] = 0;
    		for (int i = HD[u]; i; i = NXT[i]) {
    			int v = TO[i];
    			if (DIS[v] < DIS[u] + V[i]) {
    				DIS[v] = DIS[u] + V[i];
    				if (!VIS[v]) Q.push(v);
    			}
    		}
    	}
    	return DIS[r];
    } //找一条从 0 到 r 的最长路
    
    int main() {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; ++i) {
    		int x, y;
    		scanf("%d%d", &x, &y);
    		add(x, y + 1, 2);
    		r = max(y, r);
    	}
    	r++;
    	for (int i = 0; i < r; ++i) {
    		add(i, i + 1, 0);
    		add(i + 1, i, -1);
    	}
    	int ans = spfa();
    	printf("%d
    ", ans);
    	return 0;
    }
    

    小于号:

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    using namespace std;
    
    const int N = 2e4 + 5;
    const int M = 4e4 + 5;
    
    int HD[N], NXT[M], TO[M], V[M];
    int VIS[N], DIS[N];
    int n, m, r, sz;
    
    queue<int>Q;
    
    void add(int frm, int to, int val) {
    	V[++sz] = val; TO[sz] =to;
    	NXT[sz] = HD[frm]; HD[frm] = sz;
    }
    
    int spfa() {
    	memset(DIS, 127, sizeof (DIS));
    	DIS[r] = 0;
    	Q.push(r);
    	while (!Q.empty()) {
    		int u = Q.front();
    		Q.pop();
    		VIS[u] = 0;
    		for (int i = HD[u]; i; i = NXT[i]) {
    			int v = TO[i];
    			if (DIS[v] > DIS[u] + V[i]) {
    				DIS[v] = DIS[u] + V[i];
    				if (!VIS[v]) Q.push(v);
    			}
    		}
    	}
    	return -DIS[0];
    }找一条从 r 到 0 的最短路, 再取相反数
    
    int main() {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; ++i) {
    		int x, y;
    		scanf("%d%d", &x, &y);
    		add(y + 1, x, -2);
    		r = max(y, r);
    	}
    	r++;
    	for (int i = 0; i < r; ++i) {
    		add(i + 1, i, 0);
    		add(i, i + 1, 1);
    	}
    	int ans = spfa();
    	printf("%d
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    Junit单元测试
    Stream流方法引用
    Stream流思想和常用方法
    算法
    函数式接口
    Zookeeper理解
    GreenPlum学习之(Share-nothing)架构
    链表反转问题
    KMP算法的java实现
    KMP详解之二
  • 原文地址:https://www.cnblogs.com/Anding-16/p/7399855.html
Copyright © 2011-2022 走看看