HDOJ1384 链式前向星+差分约束系统

题目链接 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;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值