POJ 3680 Intervals(经典费用流)

解题思路:

区间K覆盖问题:数轴上有一些带权值的区间,选出权和尽量大的一些区间,使得任意一个点最多被K个区间覆盖。

构图方法为:把每一个数作为一个节点,然后对于权值为W的区间[ u, v ]连一条边,容量为1,费用为-w,再对所有相邻

的点连边i -> i + 1,容量为K,费用为0;最后求最左端到最右端的最小费用最大流即可。如果数值范围太大,需要先进行离散化。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <cmath>
#define LL long long
using namespace std;
const int maxn = 500 + 10;
const int INF = 0x3f3f3f3f;
struct Edge
{
	int from, to, cap, flow, cost;
	Edge(int u, int v, int c, int f, int w) : from(u), to(v), cap(c), flow(f), cost(w) { }
};
int n;
vector<Edge>edges;
vector<int>G[maxn];
int inq[maxn];
int d[maxn];
int p[maxn];
int a[maxn];
void init()
{
	for(int i=0;i<n;i++)
		G[i].clear();
	edges.clear();
}
void AddEdge(int from, int to, int cap, int cost)
{
	edges.push_back(Edge(from,to,cap,0,cost));
	edges.push_back(Edge(to,from,0,0,-cost));
	int M = edges.size();
	G[from].push_back(M-2);
	G[to].push_back(M-1);
}
bool SPFA(int s, int t, int& flow, LL& cost)
{
	for(int i=0;i<=n+1;i++)
 		d[i] = INF;
	memset(inq, 0, sizeof(inq));
	d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF;
	queue<int>Q;
	Q.push(s);
	while(!Q.empty())
	{
		int u = Q.front();Q.pop();
		inq[u] = 0;
		for(int i=0;i<G[u].size();i++)
		{
			Edge& e = edges[G[u][i]];
			if(e.cap > e.flow && d[e.to] > d[u] + e.cost)
			{
				d[e.to] = d[u] + e.cost;
				p[e.to] = G[u][i];
				a[e.to] = min(a[u], e.cap - e.flow);
				if(!inq[e.to]){Q.push(e.to);inq[e.to] = 1;}
			}
		}
	}
	if(d[t] == INF) return false;
	flow += a[t];
	cost += (LL) d[t] * (LL) a[t];
	for(int u=t;u!=s;u=edges[p[u]].from)
	{
		edges[p[u]].flow += a[t];
		edges[p[u]^1].flow -= a[t];
	}
	return true;
}
int MincostMaxflow(int s, int t, LL& cost)
{
	int flow = 0; cost = 0;
	while(SPFA(s,t,flow,cost));
	return flow;
}
struct Node
{
	int a;
	int p;
	bool operator < (const Node &rhs)const
	{
		return a < rhs.a;
	}
}li[maxn];
struct edge
{
	int u, v,w;
}ee[maxn];
int main()
{
	int T, N, K;
	scanf("%d", &T);
	while(T--)
	{
		scanf("%d%d", &N, &K);
		int u, v, w;
		for(int i=1;i<=N;i++)
		{
			scanf("%d%d%d", &u, &v, &w);
			li[2*i-1].a = u;
			li[2*i-1].p = -i;
			li[2*i].a = v;
			li[2*i].p= i;
			ee[i].u=u;
			ee[i].v=v;
			ee[i].w=w;
		}
		sort(li+1, li + 1 +2* N);
		int tmp = li[1].a,c = 1;
		for(int i=1;i<=2*N;i++)
		{
			if(li[i].a != tmp)
			{
				c++;
				tmp = li[i].a;
			}
			if(li[i].p > 0)
			{
				ee[li[i].p].v = c;
			}
			else
				ee[-li[i].p].u = c;
		}
		n = c + 1;
		init();
		for(int i=0;i<c;i++)
			AddEdge(i,i+1,K,0);
		for(int i=1;i<=N;i++)
			AddEdge(ee[i].u,ee[i].v,1,-ee[i].w);
		LL cost = 0;
		int ans = MincostMaxflow(0,c,cost);
		printf("%lld\n",-cost);
	}
	return 0;
}
时间: 2024-10-03 11:03:07

POJ 3680 Intervals(经典费用流)的相关文章

POJ 3680 Intervals 离散 + 费用流

Intervals Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 6246   Accepted: 2542 Description You are given N weighted open intervals. The ith interval covers (ai, bi) and weighs wi. Your task is to pick some of the intervals to maximize t

poj 3680 Intervals 最大费用流

题意: 给n给开区间(ai,bi)及相应权值wi,现在要选一些区间,要求任一点不能被超过k个区间覆盖,目标是最大化总的权重. 分析: 转化为求最大费用流,改改最小费用流的模板就好. 代码: //poj 3680 //sep9 #include <iostream> #include <vector> #include <queue> #include <algorithm> using namespace std; const int maxN=2048;

POJ 3670 Intervals(费用流)

POJ 3680 Intervals 题目链接 题意:给定一些区间,每个区间有一个权值,要求用这些区间去覆盖,每个点最多覆盖k次,问最多得到权值多少 思路:典型的区间k覆盖问题,区间连边容量1,代价-w,然后其他点相邻两两连边,容量k,代价0,跑一下费用流即可 代码: #include <cstdio> #include <cstring> #include <vector> #include <queue> #include <algorithm&g

POJ 2175 Evacuation Plan 费用流 负圈定理

题目给了一个满足最大流的残量网络,判断是否费用最小. 如果残量网络中存在费用负圈,那么不是最优,在这个圈上增广,增广1的流量就行了. 1.SPFA中某个点入队超过n次,说明存在负环,但是这个点不一定在负环上. 2.这个负环可能包括汇点t,所以构建残量网络的时候也要考虑防空洞到t上的容量. //#pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #include<cstring

POJ 3680 Intervals(费用流+离散化)

题目地址:POJ 3680 这题的建图真心想不出来.建图思维还是不够开阔,不够大胆. 这题要先对坐标进行离散化.可以用左边的点发出一条到右边的点的边,容量为1,费用为负的权值.然后从左往右将依次将相邻的两个点都连起来,权值为0,容量为k,也就是说,如果选了这个区间,就会从费用为负数的边流过去,否则,就是从这个费用为0的边流过去.然后建立一个超级源点与最左边的点相连,权值为0,容量为k,这样就保证了重叠数之多为k,因为增广路上所经过的区间必定是不重合的,而流量只有k,所以满足题意. 代码如下: #

poj 3680 Intervals 费用流

题目链接 给一些线段, 每个线段有一个值, 并且覆盖一些点, 求每个点被覆盖次数不超过k时, 可以取得的最大值. 首先将点离散化, 然后连边, i向i+1连一条容量为k, 费用为0的边. 对于每条线段, 起点向终点连一条容量为1, 费用为-val的边, 然后跑费用流就好. 1 #include <iostream> 2 #include <vector> 3 #include <cstdio> 4 #include <cstring> 5 #include

POJ 3680: Intervals【最小费用最大流】

题目大意:你有N个开区间,每个区间有个重量wi,你要选择一些区间,使得满足:每个点被不超过K个区间覆盖的前提下,重量最大 思路:感觉是很好想的费用流,把每个区间首尾相连,费用为该区间的重量的相反数(由于要最大,所以是求最大费用最大流),容量为1,至于不超过K的限制,只要从源点到第一个点的流量为K就行,剩下每个相邻的点相连,费用为0,流量只要大于的等于K就可以(我取的正无穷) //poj3680 #include <stdio.h> #include <iostream> #incl

POJ 2135 Farm Tour(费用流)

POJ 2135 Farm Tour 题目链接 题意:给定一个无向图,边有权值,求从1到n再从n到1的最短路 思路:费用流,连边容量为1(注意是无向图),然后源点和1连容量2,n和汇点连容量是2 代码: #include <cstdio> #include <cstring> #include <vector> #include <queue> #include <algorithm> using namespace std; const int

POJ 2175 Evacuation Plan (费用流,负环,消圈法,SPFA)

http://poj.org/problem?id=2175 Evacuation Plan Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 3256   Accepted: 855   Special Judge Description The City has a number of municipal buildings and a number of fallout shelters that were build