LOJ6045 价

人类智慧之神zhangzj最近有点胖,所以要减肥,他买了(N)种减肥药,发现每种减肥药使用了若干种药材,总共正好有(N)种不同的药材。

经过他的人脑实验,他发现如果他吃下去了(K~(0leq Kleq N))种减肥药,而这(K)种减肥药使用的药材并集大小也为(K),这(K)种才会有效果,否则无效。

(i)种减肥药在产生效果的时候会使zhangzj的体重增加(P_i)斤,显然(P_i)可以小于(0)

他想知道,一次吃药最好情况下体重变化量是多少,当然可以一种药也不吃,此时体重不变。

由于某些奥妙重重的情况,我们可以让这(N)种减肥药每一种对应一个其使用的药材,且(N)种减肥药对应的药材互不相同(即有完美匹配)。

对于(100\%)的数据,(1 leq N leq 300,~|P_i| leq 1000000)

题解

把减肥药的权值设成(-P_i+inf),把药材的权值设成(-inf),然后看成最大权闭合子点集中间连(infty)的边,跑个最小割就能得出答案了。

会不会出现选了(x)种减肥药,并且对应了(y~(y>x))种药材的情况呢?如果出现了的话,因为(y imesinf>x imes(-P_i+infty)),所以左侧一定割断了至少一条边。对应最大权闭合子点集的意义,那么左侧一定有至少一种减肥药没有被选,与假设矛盾。所以不存在这种情况,正确性有保证。

CO int64 inf=1e18,mx=1e9;
CO int N=1e3;
namespace Flow{
	int S,T;
	struct edge {int v;int64 c;int a;};
	vector<edge> to[N];
	int dis[N];
	
	IN void init(int n){
		S=n-1,T=n;
	}
	IN void link(int u,int v,int64 c){
		to[u].push_back({v,c}),to[v].push_back({u,0});
		to[u].back().a=to[v].size()-1,to[v].back().a=to[u].size()-1;
	}
	bool bfs(){
		fill(dis+1,dis+T+1,-1),dis[T]=0;
		deque<int> Q={T};
		while(Q.size()){
			int u=Q.front();Q.pop_front();
			for(CO edge&e:to[u])
				if(to[e.v][e.a].c and dis[e.v]==-1){
					dis[e.v]=dis[u]+1;
					Q.push_back(e.v);
				}
		}
		return dis[S]!=-1;
	}
	int64 dfs(int u,int64 lim){
		if(u==T) return lim;
		int64 rest=lim;
		for(edge&e:to[u])
			if(e.c and dis[e.v]==dis[u]-1){
				int delta=dfs(e.v,min(e.c,rest));
				if(!delta) {dis[e.v]=-1; continue;}
				rest-=delta,e.c-=delta,to[e.v][e.a].c+=delta;
				if(!rest) break;
			}
		return lim-rest;
	}
	int64 main(){
		int64 ans=0;
		while(bfs()) ans+=dfs(S,inf);
		return ans;
	}
}

int main(){
	int n=read<int>();
	Flow::init(2*n+2);
	for(int i=1;i<=n;++i)for(int t=read<int>();t--;)
		Flow::link(i,n+read<int>(),inf);
	int64 ans=0;
	for(int i=1;i<=n;++i){
		int64 p=read<int64>();
		Flow::link(Flow::S,i,mx-p);
		ans+=mx-p;
		Flow::link(n+i,Flow::T,mx);
	}
	ans-=Flow::main();
	printf("%lld
",-ans);
	return 0;
}
原文地址:https://www.cnblogs.com/autoint/p/12788782.html