ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

小M的作物

2021-12-26 17:33:01  阅读:171  来源: 互联网

标签:insert 连边 int edge 作物 节点 dis


luoguP1361 小M的作物

题目描述

小 M 在 MC 里开辟了两块巨大的耕地 \(A\) 和 \(B\)(你可以认为容量是无穷),现在,小 P 有 \(n\) 种作物的种子,每种作物的种子有 \(1\) 个(就是可以种一棵作物),编号为 \(1\) 到 \(n\)。

现在,第 \(i\) 种作物种植在 \(A\) 中种植可以获得 \(a_i\) 的收益,在 \(B\) 中种植可以获得 \(b_i\) 的收益,而且,现在还有这么一种神奇的现象,就是某些作物共同种在一块耕地中可以获得额外的收益,小 M 找到了规则中共有 \(m\) 种作物组合,第 \(i\) 个组合中的作物共同种在 \(A\) 中可以获得 \(c_{1,i}\) 的额外收益,共同种在 \(B\) 中可以获得 \(c_{2,i}\) 的额外收益。

小 M 很快的算出了种植的最大收益,但是他想要考考你,你能回答他这个问题么?

解法:

这是一道非常经典的二者取一类的最小割的题目。

对任意一种作物,我们让源点向他的连边的容量为在 \(A\) 中种植的收益,他向汇点的连边的容量为在 \(B\) 中种植的收益(反过来也可以)。

在这类题中,割掉的边意味着不选,留下的边是这个节点的选择,比如如果留下了这个点到汇点的连边,那么这就意味着这种作物不在 \(A\) 中种,在 \(B\) 中种。

接下来考虑如何统计同在 \(A\) 中种植的收益和同在 \(B\) 中种植的收益。因为这两种本质相同,我们就只讨论同在 \(A\) 中种植的收益。我们对新建一个节点表示这种方案,源点向这个点的连边的容量就是存在这种方案的价值,这个点再向所有包括在这种方案在内的节点连一条权值为 \(INF\) 的边。

在最小割中如果有权值为 \(INF\) 的边,就意味着这条边不可割。

为什么这样子是对的呢?对任意一个在这个方案中的节点,如果他割掉的是与源点的连边,留下的是与汇点的连边,那么这个方案所代表的节点与源点的连边也要割掉,不然就存在一条 "源点—方案节点—当前节点—汇点" 这样一条路径使源点可以到达汇点,显然这是不行的,而且我们已经割掉了和源点的连边,意味着我们 必须 要留下和汇点的连边,而方案节点和当前节点的连边权值是 \(INF\),是一条不可割边,所以我们只能把 "源点—方案节点" 这条边给割掉。

同理,同在 \(B\) 中种植的作物就是将作物代表的节点连线当前代表这个方案的节点,权值为 \(INF\);这个方案的节点连向汇点,权值是这种方案的价值。

最后求最小割就可以了。

Code:

#include <iostream>
#include <cstring>
#include <queue>
using namespace std ;

const int INF = 0x3f3f3f3f ;
int n , m , s , t , tot , dis[20005] , ans = 0 ;

struct Edge
{
	int nxt , to , len ;
} edge[4000005] ;

int cnt = 1 , head[20005] ;
void insert ( int u , int v , int w )
{
	edge [ ++ cnt ] .nxt = head [ u ] ;
	edge [ cnt ] .to = v ;
	edge [ cnt ] .len = w ;
	head [ u ] = cnt ;
}

queue < int > q ;
bool bfs ( )
{
	memset ( dis , 0 , sizeof ( dis ) ) ;
	dis [ s ] = 1 ;
	q .push ( s ) ;
	while ( ! q .empty ( ) )
	{
		int x = q .front ( ) ; q .pop ( ) ;
		for ( int i = head [ x ] ; i ; i = edge [ i ] .nxt )
		{
			int y = edge [ i ] .to ;
			if ( dis [ y ] || ! edge [ i ] .len )
				continue ;
			dis [ y ] = dis [ x ] + 1 ;
			q .push ( y ) ;
		}
	}
	return dis [ t ] ;
}

int dfs ( int x , int now )
{
	if ( x == t )
		return now ;
	int res = now ;
	for ( int i = head [ x ] ; i && res ; i = edge [ i ] .nxt )
	{
		int y = edge [ i ] .to ;
		if ( dis [ y ] != dis [ x ] + 1 || ! edge [ i ] .len )
			continue ;
		int w = dfs ( y , min ( res , edge [ i ] .len ) ) ;
		if ( ! w ) dis [ y ] = 0 ;
		edge [ i ] .len -= w ;
		edge [ i ^ 1 ] .len += w ;
		res -= w ;
	}
	return now - res ;
}

int main ( )
{
	cin >> n ; tot = n ;
	s = ++ tot ;
	t = ++ tot ;
	for ( int i = 1 ; i <= n ; ++ i )
	{
		int x ;
		cin >> x ;
		insert ( s , i , x ) ;
		insert ( i , s , 0 ) ;
		ans += x ;
	}
	for ( int i = 1 ; i <= n ; ++ i )
	{
		int x ;
		cin >> x ;
		insert ( i , t , x ) ;
		insert ( t , i , 0 ) ;
		ans += x ;
	}
	cin >> m ;
	for ( int i = 1 ; i <= m ; ++ i )
	{
		int k , c1 , c2 , si , ti ;
		cin >> k >> c1 >> c2 ;
		ans += c1 + c2 ;
		si = ++ tot ;
		ti = ++ tot ;
		insert ( s , si , c1 ) ;
		insert ( si , s , 0 ) ;
		insert ( ti , t , c2 ) ;
		insert ( t , ti , 0 ) ;
		for ( int j = 1 ; j <= k ; ++ j )
		{
			int x ;
			cin >> x ;
			insert ( si , x , INF ) ;
			insert ( x , si , 0 ) ;
			insert ( x , ti , INF ) ;
			insert ( ti , x , 0 ) ;
		}
	}
	int tmp = 0 ;
	while ( bfs ( ) )
		while ( tmp = dfs ( s , INF ) )
			ans -= tmp ;
	cout << ans << '\n' ;
	return 0 ;
}

标签:insert,连边,int,edge,作物,节点,dis
来源: https://www.cnblogs.com/linghuabaobao/p/15733494.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有