[题解] poj 1716 Integer Intervals (差分约束+spfa)
- 传送门 -
http://poj.org/problem?id=1716
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
[Submit] [Status] [Discuss]
- 题意 -
有 n 个区间.
求一个集合, 使得每一个区间都至少有两个数在该集合中.
问集合中最少有几个数.
- 思路 -
有思想的差分约束.
差分约束的详解: http://www.cppblog.com/menjitianya/archive/2015/11/19/212292.html
首先设 \(S[x]\) 表示 \(0\to 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\to^{v1} a\to^{v2} b\to^{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\n", 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\n", ans);
return 0;
}