P3410 拍照 题解

文章讲述了如何将一个关于分配金钱给属下,满足每个人需求且保证自己收益的问题转化为图论中的网络流问题。通过构建源点到拍照者,以及个体到其属下的边,使用最大流算法求解最终能获取的最大收益。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大家在其他题解中看到的直接告诉你这个是什么类型的题目,用什么概念去求,让后就放代码,在这里我会用通俗易懂的话来讲这道题为什么这样建图。

读题可知一个事情,如果要使得可以让这个来拍照的人拍照只有两种可能性:

  1. 给的钱已经大于了所需要的属下需要的钱。
  2. 经过其他与之有交集(跟他有相同需要属下的人)的属下总共的和大于需要的属下总共需要的钱。

我们就可以将其题目转化成有 M M M 个人他们可以给某些属下发钱,他们有 N N N 个属下,其中每个属下想要的钱是固定的。我们可以让 M M M 个人中任意个数的人给他们想发钱的属下(可以不发满)如果全部属下都不能再收钱那么就将钱给你,求在每个属下都发满的前提下你能得到多少钱。

这样的话,我们就只需要假设这 M M M 个人都发钱,让后求每个人手上剩下的钱。这也就变成了求 N N N 个属下可以得到多少钱,之后让 M M M 个人能发的钱的总和减去 N N N 个属下总共能得到的钱就是本题的答案。

所以我们就可以用网络流来求解,将源点与拍照的人相连,流量为他们给的钱,再将他们与他们所需要的属下相连,流量为无穷大,最后将属下连向汇点。跑一个最大流,让 M M M 个人给的钱的总和减去最大流就为答案了。

代码

#include<bits/stdc++.h>
#define int long long 
#define inf 2147483647
#define N 223 
using namespace std ;
int n , m , head[N] , now[N] , cnt=0 , s , t , dep[N] , ans=0 , sum=0 ;
bool f[N] ;
struct node{
	int to , next , w ;
}e[N<<12]; //谁会认真的算这个东西 
void newnet(int u,int v,int w){
	e[cnt].to = v ;
	e[cnt].w = w ;
	e[cnt].next = head[u] ;
	head[u] = cnt++ ;
}
bool bfs(){
	for(int i=1;i<=t;i++) dep[i] = inf , now[i] = head[i] , f[i] = 0 ;
	queue<int> q ;
	q.push(s) ;
	dep[s] = 0 ;
	while(!q.empty()){
		int u=q.front() ;
		q.pop() ;
		f[u] = 0 ;
		for(int i=head[u];~i;i=e[i].next){
			int x=e[i].to ;
			if(dep[u]+1<dep[x]&&e[i].w){
				dep[x] = dep[u]+1 ;
				if(!f[x]){
					f[x] = 1 ;
					q.push(x) ;
				}
			}
		}
	}
	return dep[t]!=inf ;
}
int dfs(int u,int sum){
	if(u==t) return sum ;
	int use=0 ;
	for(int i=now[u];~i;i=e[i].next){
		now[u] = i ;
		int x=e[i].to ;
		if(dep[x]!=dep[u]+1||!e[i].w) continue ;
		int temp=dfs(x,min(sum,e[i].w)) ;
		if(!temp) continue ;
		e[i].w -= temp ;
		e[i^1].w += temp ;
		use += temp ;
		if(use==sum) break ;
	}
	return use ;
}
void dinic(){
	while(bfs())
		ans += dfs(s,inf) ;
	cout << max(sum-ans,1ll*0) << endl ;
}
int read(){
	int x=0 , f=1 ;
	char a=getchar() ;
	while(!(a>='0'&&a<='9')){
		if(a=='-') f = -f ;
		a = getchar() ;
	}
	while(a>='0'&&a<='9'){
		x *= 10 ;
		x += a-'0' ;
		a = getchar() ;
	}
	return x ;
}
signed main(){
	memset(head,-1,sizeof(head)) ;
	cin >> m >> n ;
	s = n+m+1 ;
	t = s+1 ;
	for(int i=1;i<=m;i++){
		int c=read() , to ;
		sum += c ;
		newnet(s,i,c) ;
		newnet(i,s,0) ;
		while(to=read()){
			newnet(i,m+to,inf) ;
			newnet(m+to,i,0) ;
		}
	}
	for(int i=1;i<=n;i++){
		int c=read() ;
		newnet(i+m,t,c) ;
		newnet(t,i+m,0) ;
	}
	dinic() ;
	return 0 ;
}

在最后祝在谷CS2玩家能像我一样,哥本哈根胶囊一发出粉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值