HDU6598 Harmonious Army

本文详细解析了一道关于最大贡献关系的问题,通过建立图模型并运用最小割算法求解最大贡献值,介绍了如何处理复杂的人员关系和贡献计算,适用于算法竞赛和图论学习。

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

一、题目

题目描述
n n n个人, m m m个关系,每个关系中包含 u , v , a , b , c u,v,a,b,c u,v,a,b,c,表示 u , v u,v u,v两个人,如果都选 0 0 0 a a a的贡献,如果都选 1 1 1 c c c的贡献,否则有 b b b的贡献,求最大的贡献。
数据范围
n ≤ 500 , m ≤ 1 0 4 , 1 ≤ u , v ≤ n , u ≠ v , 1 ≤ a , c ≤ 4 × 1 0 6 , b = a / 4 + c / 3 n≤500,m≤10^4,1≤u,v≤n,u≠v,1≤a,c≤4×10^6,b=a/4+c/3 n500,m104,1u,vn,u=v,1a,c4×106,b=a/4+c/3
本题有多组输入数据

二、解法

这道题很暴力,考虑总量-最小割,直接建一个图,然后解方程算出每条边的贡献,图长这样:
在这里插入图片描述
{ a + b = A + B c + d = B + C a + e + d = b + e + c = A + C \begin{cases} a+b=A+B\\ c+d=B+C\\ a+e+d=b+e+c=A+C \end{cases} a+b=A+Bc+d=B+Ca+e+d=b+e+c=A+C

上面的方程肯定是解不出来的,但是我们求出一个特殊解就行了:

{ a = b = ( A + B ) / 2 c = d = ( B + C ) / 2 e = ( A + C ) / 2 − B \begin{cases} a=b=(A+B)/2\\ c=d=(B+C)/2\\ e=(A+C)/2-B \end{cases} a=b=(A+B)/2c=d=(B+C)/2e=(A+C)/2B

然后把图建出来之后跑最小割,用 s u m − f l o w sum-flow sumflow就是答案,可能会有边权不是整数,所以我们把所有边权 × 2 \times 2 ×2,然后算答案是把 f l o w / 2 flow/2 flow/2即可,本题要看 long long \text{long long} long long

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#define int long long
#define inf (1ll<<60)
const int MAXN = 1005;
using namespace std;
int read()
{
    int num=0,flag=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
    return num*flag;
}
int n,m,S,T,tot,sum,ans,f[MAXN],cur[MAXN],dis[MAXN];
queue<int> q;
struct edge
{
	int v,c,next;
}e[MAXN*MAXN];
void add_edge(int u,int v,int c)
{
	e[++tot]=edge{v,c,f[u]},f[u]=tot;
	e[++tot]=edge{u,0,f[v]},f[v]=tot;
}
int bfs()
{
	memset(dis,0,sizeof dis);
	dis[S]=1;
	q.push(S);
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=f[u];i;i=e[i].next)
		{
			int v=e[i].v;
			if(e[i].c>0 && !dis[v])
			{
				dis[v]=dis[u]+1;
				q.push(v);
			}
		}
	}
	if(!dis[T]) return 0;
	return 1;
}
int dfs(int u,int ept)
{
	if(u==T) return ept;
	int flow=0,tmp=0;
	for(int &i=cur[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(dis[u]+1==dis[v] && e[i].c>0)
		{
			tmp=dfs(v,min(e[i].c,ept));
			if(!tmp) continue;
			ept-=tmp;
			e[i].c-=tmp;
			e[i^1].c+=tmp;
			flow+=tmp;
			if(!ept) break;
		}
	}
	return flow;
}
signed main()
{
	while(~scanf("%lld %lld",&n,&m))
	{
		S=0;T=n+1;tot=1;sum=ans=0;
		for(int i=0;i<=T;i++)
			f[i]=0;
		for(int i=1;i<=m;i++)
		{
			int u=read(),v=read(),a=read(),b=read(),c=read();
			sum+=a+b+c; 
			add_edge(S,u,(a+b));
			add_edge(S,v,(a+b));
			add_edge(u,v,(a+c)-b*2);
			add_edge(v,u,(a+c)-b*2);
			add_edge(u,T,(b+c));
			add_edge(v,T,(b+c));
		}
		while(bfs())
		{
			for(int i=0;i<=T;i++)
				cur[i]=f[i];
			ans+=dfs(S,inf);
		}
		printf("%lld\n",sum-ans/2);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值