分配(dispatch)

题目大意:有两种人,分别Na,Nb个,两个地方,每个人都要去一个地方,每个人到一个地方都可以获得一个权值(绝对值小于1000),如果不同的一种人去了同一个地方,需要减掉一个权值c[i][j](大于等于0) ,求最大的权值还有在权值最大的情况下每个人去了哪里  Na,Nb<=200


这题很神,首先要权值ans=w(去一个地方获得的)-c(因为两种不同的人减去的)最大,我们可以让-ans=c-w

这样就只要让-ans最小就OK了,因为(-w)有正有负,所以就都先加上一个值,最后在减去就OK了,于是就是让-ans=c+(-w)最小

这就可以用最小割来做了具体来说,X种人在S集是去A地,T集是去B地,Y种人是在S集去B地,T集去A地,然后就可以连边了。。。

至于方案就看最后的每个点在S集还是T集,在结合上面的定义就OK了


#include<cstdio>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=444,maxm=100011,inf=~0u>>1,aw=1024;
inline int read(){
	int x=0; char ch=getchar(); bool ok=0;
	while (!isdigit(ch) && ch!='-') ch=getchar();
	if (ch=='-') ch=getchar(),ok=1;
	for (;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
	return ok?-x:x;
}
int tot=1,now[maxn],pre[maxm],son[maxm],v[maxm];
void add(int a,int b,int c){pre[++tot]=now[a]; now[a]=tot; son[tot]=b; v[tot]=c;}
void cc(int a,int b,int c,int d=0){add(a,b,c); add(b,a,d?c:0);}
int n,m,st,ed;
void init(){
	n=read(); m=read(); st=n+m+1; ed=st+1;
	for (int i=1;i<=n;++i) cc(st,i,aw-read()); for (int i=1;i<=m;++i) cc(i+n,ed,aw-read());
	for (int i=1;i<=n;++i) cc(i,ed,aw-read()); for (int i=1;i<=m;++i) cc(st,i+n,aw-read());
	for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) cc(i,n+j,read(),1);
}
int dep[maxn];
bool bfs(){
	memset(dep,0,sizeof(dep)); dep[st]=1;
	int t=0,w=1; static int q[maxn]; q[1]=st;
	while (t++<w){
		for (int p=now[q[t]];p;p=pre[p]) if (!dep[son[p]] && v[p])
			dep[q[++w]=son[p]]=dep[q[t]]+1;
		if (dep[ed]) return 1;
	}
	return 0;
}
int find(int x,int f){
	if (x==ed) return f; int ans=0;
	for (int p=now[x];p;p=pre[p]) if (dep[son[p]]>dep[x] && v[p]){
		int k=find(son[p],min(f,v[p]));
		v[p]-=k; v[p^1]+=k; ans+=k; f-=k;
		if (!f) return ans;
	}
	dep[x]=0; return ans;
}
int zg(){int ans=0; while (bfs()) ans+=find(st,inf); return ans;}
void work(){
	printf("%d\n",1024*(n+m)-zg());
	bfs(); for (int i=1;i<=n;++i) if (dep[i]) printf("2 ");else printf("1 ");
	for (int i=1;i<=m;++i) if (dep[i+n]) printf("1 ");else printf("2 ");
}
int main(){
	init();
	work();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值