codeforces103E Buying Sets

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

题目链接:codeforces103E

正解:网络流+最小权闭合图

解题报告:

  考虑这道题的模型。

  首先因为存在完备匹配,所以可以给每个点找一个匹配点,然后这个点向所需要的药材的匹配点连边。

  然后我们可以得到一个选择一个点就要选择它的所有出边的最小闭合权子图的模型,取个负号跑最大权闭合子图就好了,直接套板子。

  这样建图的正确性可以意会一下,很明显可以起到选一个就选另一个的效果。

  补一发最大权闭合子图的理解:

  很久以前学过但是忘得差不多了。

  这类题的模型就是一个每个点带权的有向图,选择了一个点就要选择他的所有出边,最大化选出的点的最大权值和。

  建图的方法就是$S$向正权点连容量为点权的边,原图的边容量为$inf$,负权点向$T$连容量为点权的绝对值的边。

  先默认选择所有的正权点,$ans$的初值就是正权点的点权之和,而实际情况下可能有些正权点不选,还要另选择一些负权点。

  那么$ans=ans-$(不选择正权$+$选择的负权的绝对值),后者就是图的最小割,也就是最大流。

  考虑最小割只能与$S$或$T$相连。

  与$S$相连的点说明供应$<=$出边流量,那么显然是不选择的正权,因为选了不划算;

  与$T$相连的说明,为其提供流量的边还未满,那么这个负权就是选择了正权之后付出的一些负权点代价,显然会划算。

//It is made by ljh2000
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <string>
#include <queue>
#include <cmath>
#include <ctime>
using namespace std;
typedef long long LL;
const int MAXN = 15001;
const int inf = (1<<29);
int n,P[MAXN],Tim,match[MAXN],vis[MAXN];
vector<int>medi[MAXN];
LL ans;

inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<‘0‘||c>‘9‘) && c!=‘-‘) c=getchar();
    if(c==‘-‘) q=1,c=getchar(); while (c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); return q?-w:w;
}

namespace network{
	const int MAXM = 1000011;
	int ecnt,first[MAXN],S,T,dui[MAXN],deep[MAXN];
	queue<int>q;
	struct edge{ int to,f,next; }e[MAXM];
	inline void Init(){ ecnt=1; S=n+1; T=S+1; }
	inline void link(int x,int y,int z){
		e[++ecnt].next=first[x]; first[x]=ecnt; e[ecnt].to=y; e[ecnt].f=z;
		e[++ecnt].next=first[y]; first[y]=ecnt; e[ecnt].to=x; e[ecnt].f=0;
	}

	inline bool bfs(){
		for(int i=1;i<=T;i++) deep[i]=-1; deep[S]=0; q.push(S); int u;
		while(!q.empty()){
			u=q.front(); q.pop();
			for(int i=first[u];i;i=e[i].next) {
				if(e[i].f==0) continue;
				int v=e[i].to; if(deep[v]!=-1) continue;
				deep[v]=deep[u]+1; q.push(v);
			}
		}
		return (deep[T]!=-1);
	}

	inline int dinic(int x,int remain){
		if(x==T || remain==0) return remain;
		int f,flow=0;
		for(int i=first[x];i;i=e[i].next) {
			if(e[i].f==0) continue;
			int v=e[i].to; if(deep[v]!=deep[x]+1) continue;
			f=dinic(v,min(remain,e[i].f));
			if(f==0) deep[v]=-1;
			else {
				flow+=f; remain-=f;
				e[i].f-=f; e[i^1].f+=f;
				if(remain==0) return flow;
			}
		}
		return flow;
	}
}

inline bool dfs(int x){
	if(vis[x]==Tim/*!!!*/) return false;//!!!
	vis[x]=Tim;
	for(int i=0,ss=medi[x].size();i<ss;i++) {
		int v=medi[x][i]; if(vis[v]==Tim) continue;
		if(!match[v] || dfs(match[v])) {
			match[v]=x;
			return true;
		}
	}
	return false;
}

inline void work(){
	using namespace network;
	n=getint(); Init(); int x,y;
	for(int i=1;i<=n;i++) { x=getint(); while(x--) { y=getint(); medi[i].push_back(y+n); } }
	for(int i=1;i<=n;i++) Tim++,dfs(i);
	for(int i=1;i<=n;i++) P[i]=getint();
	for(int i=1;i<=n;i++) for(int j=0,ss=medi[i].size();j<ss;j++) link(i,match[ medi[i][j] ],inf);
	for(int i=1;i<=n;i++) {
		P[i]=-P[i];
		if(P[i]>0) link(S,i,P[i]),ans+=P[i];
		else link(i,T,-P[i]);
	}

	while(bfs())
		ans-=dinic(S,inf);

	ans=-ans;
	printf("%I64d",ans);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("103.in","r",stdin);
	freopen("103.out","w",stdout);
#endif
    work();
    return 0;
}
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。

  

时间: 2024-12-10 19:47:04

codeforces103E Buying Sets的相关文章

CF 103E Buying Sets 最大权闭合子图,匹配 难度:4

http://codeforces.com/problemset/problem/103/E 这道题首先一看就很像是最大权闭合子图,但是我们可以认为现在有两种点,数字和集合点,我们需要消除数字点的影响才能直接运用最大权闭合子图. 进行二分匹配,使得每个集合都唯一匹配一个数字,买下一个集合点,则意味着该集合中所有数字的对应匹配集合点都要被买下,也就是可以建立一个新图,其中某个集合点向对应数字代表的集合点连单向边,可以证明对于任意权闭合子图中的集合点,集合中所有数字的对应匹配集合点都已经在这个权闭合

【codeforces 103E】 Buying Sets

http://codeforces.com/problemset/problem/103/E (题目链接) 题意 给出$n$个数,每个数与一个集合相关联.从其中选出最小的若干个数,选出的数的个数与这些数相关联的集合的并集大小相等. Solution 因为保证了有完全匹配,所以跑出一个完全匹配,这样我们可以知道,如果选了某一个数,必须要选的其它数是哪些.但是问题是随着匹配的变化,这种关系会不会发生变化呢?是不会的.画画图,你会发现无论如何连边,最后都是从一个点走出去经过那些点再从一条非匹配边走回来

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

Ever wonder about that mysterious Content-Type tag? You know, the one you're supposed to put in HTML and you never quite know what it should be? Did you ever get an email from your friends in Bulgaria with the subject line "???? ?????? ??? ????"

Simple Automated Backups for MongoDB Replica Sets

There are a bunch of different methods you can use to back up your MongoDB data, but if you want to avoid downtime and/or potential performance degradation, the most common advice seems to be that you should simply do all your backups on a slave. Thi

[BZOJ] 1618: [Usaco2008 Nov]Buying Hay 购买干草

1618: [Usaco2008 Nov]Buying Hay 购买干草 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1216  Solved: 633[Submit][Status][Discuss] Description 约翰的干草库存已经告罄,他打算为奶牛们采购H(1≤H≤50000)磅干草,他知道N(1≤N≤100)个干草公司,现在用1到 N给它们编号.第i个公司卖的干草包重量为Pi(1≤Pi≤5000)磅,需要的开销为Ci(l≤Ci≤5

The Tree-planting Day and Simple Disjoint Sets

First I have to say: I have poor English. I am too young, too simple, sometimes na?ve. It was tree-planting day two weeks ago. SHENBEN dph taught us a lot about tree-planting and the disjoint sets. It was useful and valuable for a JURUO like me. I ad

并查集 (Union-Find Sets)及其应用

定义 并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.常常在使用中以森林来表示. 集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并. 主要操作 初始化 把每个点所在集合初始化为其自身. 通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N). 查找 查找元素所在的集合,即根节点. 合并 将两个元素所在的集合合并为一个集合. 通常来说,合并之前,应先判断两个元素是否属于

hdu 3836 Equivalent Sets【强联通】

Equivalent Sets Time Limit: 12000/4000 MS (Java/Others)    Memory Limit: 104857/104857 K (Java/Others) Total Submission(s): 3065    Accepted Submission(s): 1077 Problem Description To prove two sets A and B are equivalent, we can first prove A is a s

UVA 11488 Hyper Prefix Sets (Trie)

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2483 Hyper Prefix Sets Prefix goodness of a set string islength of longest common prefix*number of strings in the set.For example the prefix goodnes