题目
http://poj.org/problem?id=1201
题意
给你n个整数区间(5e4数量级),要求给出一个最小的整数集合,这个整数集合至少与第i个区间有ci个重合整数
思路
很裸的差分约束,若令dp[w]为在[0, w]中取多少个整数,明显dp[bi] - dp[ai-1] = ci。那么现在还缺乏能够灵活调控具体数字的其他约束。这里选择了0<=dp[i]-dp[i - 1] <=1。
从一个非可行解出发,比如dp[w], w=[1, mx],这里mx是最大的区间右端点,那么明显求满足约束的最大对偶解就能确保其可行,对应可行解的最小解。
感想
- 这道题RE会报TLE!之前把first[MAXL]写成了first[MAXN],怎么也找不出为什么别人的能过自己的TLE
- 一开始想成了01整数规划。看来要多理解差分约束相关的思路
Code
3596K 329MS
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <assert.h>
using namespace std;
const int MAXN = 5e4;
const int MAXL = 5e4 + 2;
const int MAXE = MAXL * 3;
int n, mx, mn;
int a[MAXN];
int b[MAXN];
int c[MAXN];
int e;
int first[MAXL];
int nxt[MAXE];
int to[MAXE];
int cost[MAXE];
int dp[MAXL];
bool used[MAXL];
int que[MAXL];
void addEdge(int f, int t, int c) {
nxt[e] = first[f];
to[e] = t;
cost[e] = c;
first[f] = e++;
}
int main() {
freopen("C:\Users\Iris\source\repos\Project1\input.txt", "r", stdin);
freopen("C:\Users\Iris\source\repos\Project1\out.txt", "w", stdout);
e = 0;
memset(first, -1, sizeof first);
mn = MAXL;
scanf("%d", &n);
for (int i = 0; i < n; i++){
scanf("%d%d%d", a + i, b + i, c + i);
a[i] ++;
b[i] ++;
mx = max(mx, b[i]);
mn = min(mn, a[i] - 1);
if(c[i] > 0)addEdge(b[i], a[i] - 1, c[i]);
}
for (int i = mn + 1; i <= mx; i++) {
addEdge(i - 1, i, -1);
addEdge(i, i - 1, 0);
dp[i] = -1e9;
}
int quewnum = 0;
int quernum = 0;
dp[mx] = 0;
used[mx] = true;
que[(quewnum++) % MAXL] = mx;
while (quernum < quewnum) {
int f = que[(quernum++) % MAXL];
used[f] = false;
for (int p = first[f]; p != -1; p = nxt[p]) {
int t = to[p];
int c = cost[p];
if (dp[t] < dp[f] + c) {
//printf("%d(dp: %d)->%d(dp:%d/%d) %d
", f, dp[f], t, dp[t], dp[f] + c, c);
dp[t] = dp[f] + c;
if (!used[t]) {
que[(quewnum++) % MAXL] = t;
used[t] = true;
}
}
}
}
printf("%d
", dp[mn]);
return 0;
}