TYVJ 武器分配

描述 Description
    后勤部队运来一批武器(机枪和盔甲)。你要把这些武器分配给手下的marine们(每人一部机枪,一套盔甲)。可是问题来了。。。
    这些武器的型号不相同(武器是由出价最低的承包商制造的),把一部m型的机枪和一套n型的盔甲分配给一个marine得到的不满意值为(m-n)^2(每个marine当然希望自己得到的武器是同一型号的)。
    你的任务就是把a部机枪和b套盔甲分配给手下n个marine。使他们的不满意值之和最小。
输入格式 InputFormat
第一行:3 个正整数 n , a , b (1<=n<=a,b<=80)
第二行:a 个数表示每部机枪的型号
第三行:b 个数表示每套盔甲的型号
0<=型号值<=10000
输出格式 OutputFormat
输出一个数:最小不满意值。
样例输入 SampleInput [复制数据]
Sample 1:
2 3 3
9 10 20
0 10 11
Sample 2:
3 4 4
3 9 7 4
4 2 5 5
样例输出 SampleOutput [复制数据]
Sample 1:
2
Sample 2:
5

题解

第一眼看有点像二分图匹配,但是有限定人数。所以用费用流。

直接在所有机枪和所有防弹衣之间连上有向边,边的费用为(a[i]-b[j])^2,容量为1。再加上超级源点0和第一汇点T1,费用为0,容量为1。

最后再加一个第二汇点T2,超级汇和第二汇点相连,费用为0,容量为n,用来进行人数限制。

最后来一次最小费用最大流就可以了。

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
#define inf 0x7fffffff
using namespace std;
int n,a,b,x[82],y[82],T1,T2,zz=1,head[201];
struct bian {int frm,to,v,nx,c;} e[50000];
int from[201],dis[201],q[201],ans;
bool pd[201];
void insert(int u,int v,int w,int z)
{
	zz++; e[zz].frm=u; e[zz].to=v;
	e[zz].v=w; e[zz].c=z; e[zz].nx=head[u]; head[u]=zz;
	zz++; e[zz].frm=v; e[zz].to=u;
	e[zz].v=0; e[zz].c=-z; e[zz].nx=head[v]; head[v]=zz;
}
void build()
{
	for(int i=1;i<=a;i++)
	for(int j=1;j<=b;j++)
	   insert(i,j+a,1,(x[i]-y[j])*(x[i]-y[j]));
	for(int i=1;i<=a;i++) insert(0,i,1,0);
	for(int i=1;i<=b;i++) insert(i+a,T1,1,0);
	insert(T1,T2,n,0);
}
void init()
{
	scanf("%d%d%d",&n,&a,&b);
	for(int i=1;i<=a;i++) scanf("%d",&x[i]);
	for(int j=1;j<=b;j++) scanf("%d",&y[j]);
	T1=a+b+1; T2=T1+1;
}
bool spfa()
{
	for(int i=0;i<=T2;i++) dis[i]=inf;
	int t=0,w=1,i;
	q[0]=dis[0]=0; pd[0]=1;
	while(t!=w)
	   {int p=q[t]; t=(t+1)%200;
	    i=head[p];
	    while(i)
	       {if(e[i].v&&dis[e[i].to]>dis[p]+e[i].c)
		       {from[e[i].to]=i;
			    dis[e[i].to]=dis[p]+e[i].c;
			    if(!pd[e[i].to])
			       {q[w]=e[i].to; pd[e[i].to]=1; w=(w+1)%200;}
			   }
			i=e[i].nx;
		   }
		pd[p]=0;
	   }
	if(dis[T2]==inf) return 0;
	return 1;
}
void getf()
{
	int i,xx=inf;
	i=from[T2];
	while(i)
	   {xx=min(e[i].v,xx); i=from[e[i].frm];}
	i=from[T2];
	while(i)
	   {e[i].v-=xx; e[i^1].v+=xx;
		ans+=xx*e[i].c; i=from[e[i].frm];
	   }
}
void mcf()
{while(spfa()) getf();}
int main()
{
	init(); build(); mcf();
	printf("%d",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值