题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1384
Problem 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 endpoints 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 <= 50 000) - 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 <= 50 000 and 1 <= ci <= bi - ai + 1.
Process to the end of file.
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
题意: 给定n个自然数区间[ai,bi],从区间中至少选出ci个整数,最终选出的所有数组成的集合S,S集合元素个数最少是多少?
题解: 刚在网上学了下差分约束系统,学的时候感觉比较简单,一做题就不行了,这道题看到后想了下,感觉和差分约束联系不起来,一看大佬们的题解,才恍然大悟。。。
设变量xi表示[0,i-1]中至少选择多少个数,则题目中的条件可以转化为不等式 Xbi+1 - Xi >= ci,再加上隐形条件
0 <= Xi - Xi-1 <= 1,这样就构造出了完成的差分约束的不等式。(只能说大佬们智商太高,我反正想不到)
然后直接构造图,用SPFA跑一遍最长路径即可。
不过这题比较坑的是,貌似不能使用vector来建邻接表(会被卡时间),我刚开始用vector建表,一直超时,后来看了几篇别人的博客才明白是STL被卡掉了(vector是动态数组,每次扩容会扩充1.5倍,不论在时间还是空间上都有会有一些白白的损失),所以这题得改用链式前向星来存图才行。本来我都不会链式前向星的,做了这题长知识了。。
可以参考博客 https://www.cnblogs.com/gj-Acit/p/3261721.html ,我的代码思路基本是照着他的写的
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#define MAXN 50010
#define INF 99999999
#define INT_MIN -99999999
#define INT_MAX 99999999
using namespace std;
int n;
int Min = INT_MIN,Max = INT_MAX;
int dis[MAXN];
struct Edge {
int next;
int v;
int w;
}edge[3*MAXN];
int head[MAXN];
queue<int> q;
bool vis[MAXN];
int N;//N条边
// 加边
void AddEdge(int u,int v,int w) {
edge[N].v = v;
edge[N].w = w;
edge[N].next = head[u];
head[u] = N++;
}
// 初始化
void Init() {
Min = INT_MAX,Max = INT_MIN;
while(!q.empty()) q.pop();
memset(vis,0,sizeof(vis));
for(int i = 0;i < MAXN;i++) head[i] = -1;
N = 0;
}
// SPFA求最长路
void SPFA() {
q.push(Min);
dis[Min] = 0;
vis[Min] = true;// 标记入队
for(int i = Min+1;i <= Max;i++) dis[i] = -INF;
while(!q.empty()) {
int front = q.front();
q.pop();
vis[front] = false;
for(int e = head[front];e != -1;e = edge[e].next) {
if(dis[edge[e].v] < dis[front] + edge[e].w) {
dis[edge[e].v] = dis[front] + edge[e].w;
if(vis[edge[e].v] == false) {
q.push(edge[e].v);
vis[edge[e].v] = true;
}
}
}
}
}
int main()
{
int a,b,c;
while(scanf("%d",&n) != EOF) {
Init();
for(int i = 0;i < n;i++) {
scanf("%d%d%d",&a,&b,&c);
AddEdge(a,b+1,c);
Max = max(Max,b+1);
Min = min(Min,a);
}
for(int i = Min;i < Max;i++) {
AddEdge(i,i+1,0);
AddEdge(i+1,i,-1);
}
SPFA();// 求最长路
printf("%d\n",dis[Max]);
}
return 0;
}