HDU - 1384 差分约束

题意:

要求选取一个最小的集合,集合中的数满足n个条件,每个条件:在区间[ai,bi]内至少有ci个数备选在了集合里。

思路:

差分约束,设sum[i]表示在i-1中选取到集合中的数的个数,那么题目的要求就可以描述为如下的关系:
sum[a] - sum[b+1] <= -c
sum[i] - sum[i+1] <= 0
sum[i+1] - sum[i] <= -1
这样就可以转化成关于查分约束的模式了,因为一共有-1到50000一共50002个点,这样可以求出sum[0] - sum[50001] <= -x,-x的最大值,取个倒数也就是x的最小值,表示在0
到50000的范围内最少选多少个数。因为边上包含负数,可能有负环,所以要用spfa来求解。

代码:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
using namespace std;
const int MAXN = 51111;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

struct Edge {
	int from, to, dist;
};

struct SPFA {
	int n, m;
	vector <Edge> edges;
	vector <int> G[MAXN];
	bool vis[MAXN];
	int d[MAXN], p[MAXN];
	int cnt[MAXN];

	void init(int n) {
		this -> n = n;
		for (int i = 0; i < n; i++) G[i].clear();
		edges.clear();
	}

	void AddEdge(int from, int to, int dist) {
		edges.push_back((Edge) {from, to, dist});
		m = edges.size();
		G[from].push_back(m - 1);
	}

	bool solve(int s) {
		queue <int> q;
	    memset (vis, false, sizeof vis);
	    memset (cnt, 0, sizeof(cnt));
	    for (int i = 0; i < n; i++) d[i] = INF;
	    vis[s] = true; d[s] = 0; cnt[s] = 1; q.push (s);
	    while (!q.empty ()) {
	        int u = q.front (); q.pop ();
	        vis[u] = false;
	        for (int i = 0; i < (int)G[u].size(); i++) {
	            Edge& e = edges[G[u][i]];
	            int v = e.to, w = e.dist;
	            if (d[v] > d[u] + w) {
	                d[v] = d[u] + w;
	                if (!vis[v]) {
	                    vis[v] = true; q.push (v);
	                    if (++cnt[v] > n)
	                        return false;
	                }
	            }
	        }
	    }
	    return true;
	}

} spfa;

int main() {
	int n;
	while (scanf("%d", &n) != EOF) {
		spfa.init(50002);
		for (int i = 1; i <= n; i++) {
			int u, v, w;
			scanf("%d%d%d", &u, &v, &w);
			spfa.AddEdge(v + 1, u, -w);
		}
		for (int i = 0; i <= 50000; i++) {
			spfa.AddEdge(i + 1, i, 0);
			spfa.AddEdge(i, i + 1, 1);
		}
		spfa.solve(50001);
		printf("%d\n", abs(spfa.d[0]));
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值