POJ 2516

这道题目最好的方法应该是网络流来解决。但网络流社么都不记得了,所以用二分图做的。

还是不会建模。。。每个商定所需的物品可能来自不同的仓库,细化就是每一类物品可能来自不同的仓库,再细化就是每一个物品在最后的结果中都有一个在某个仓库中的物品与其匹配。所以对于每一个物品建点,商店的为x集合,仓库的为y集合。在相同的物品见连线,最后用KM求解。其实题目已经给出了提示,k很小0~3,直接对每个物品建图即可。

把所有的点按一个整体求了一次KM,超时(虽然已经知道会超时了),然后整个图其实是有几个没有任何边相互连接的的独立的子图构成的,每一类物品构成一个独立的子图,所以可以对每个子图分别的进行最大完美匹配。

还有一个就是每一类的物品在商店中的数目如果大于在仓库中的数目的话,直接跳出终止,不成立。但认为最坏的情况下也没什么效果,所以就没加,超时。加了之后200多ms,以后能优化的地方要尽量优化。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#define LL long long
#define myabs(x) ((x)>0?(x):(-(x)))
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=50*3+10;
struct Edge { int v,next; };
int g[maxn][maxn];
const int maxm=50+10;
int dis[maxm][maxm][maxm];
struct node { int num,be; };
node ma1[maxn],ma2[maxn];
int n,m,k;
int loc1[maxn][maxn],loc2[maxn][maxn];
int amount1[maxn],amount2[maxn];
int S[maxn],T[maxn];
int lv[maxn],rv[maxn],leftp[maxn];
int slack;
int tot1,tot2;
void update()
{
	int i;
	for(i=1;i<=tot1;i++) if(S[i]) lv[i]-=slack;
    for(i=1;i<=tot2;i++)  if(T[i]) rv[i]+=slack; 
}
int match(int u)
{
    S[u]=1;
	int i,j;
	for(i=1;i<=tot2;i++)
    {
		if(lv[u]+rv[i]==g[u][i])
        {
            if(!T[i])
            {
                T[i]=1;
                if(leftp[i]==-1||match(leftp[i]))
                {
                    leftp[i]=u;
                    return 1;
                }
            }
        }
		else slack=min(lv[u]+rv[i]-g[u][i],slack);
    }
    return 0;
}

int  solve()
{
    int i,j;
	memset(leftp,-1,sizeof(leftp));
	for(i=1;i<=tot1;i++) lv[i]=0;
	for(i=1;i<=tot2;i++) rv[i]=0;
	for(i=1;i<=tot1;i++)
    {
		for(j=1;j<=tot2;j++)
			lv[i]=max(lv[i],g[i][j]);
    }
	for(i=1;i<=tot1;i++)
    {
        for(;;)
        {
			for(j=1;j<=tot1;j++)  S[j]=0;
			for(j=1;j<=tot2;j++) T[j]=0;
            slack=inf;
            if(match(i)) break;
			if(slack==inf) return -inf; 
			update();
        }
    }
    int sum=0;
    for(i=1;i<=tot1;i++) sum+=lv[i];
	for(i=1;i<=tot2;i++) sum+=rv[i];
    return sum;
}
int main()
{
	while(~scanf("%d%d%d",&n,&m,&k))
	{
		if(!n&&!m&&!k) break;
		int i,j;
		memset(amount1,0,sizeof(amount1));
		memset(amount2,0,sizeof(amount2));
		int tem,p;
		for(i=1;i<=n;i++)
			for(j=1;j<=k;j++)
			{
				scanf("%d",&tem);
				for(p=0;p<tem;p++)
					loc1[j][++amount1[j]]=i;
			}
		for(i=1;i<=m;i++)
		{
			for(j=1;j<=k;j++)
			{
				scanf("%d",&tem);
				for(p=0;p<tem;p++)
					loc2[j][++amount2[j]]=i;
			}
		}
		for(p=1;p<=k;p++)
			for(i=1;i<=n;i++)
				for(j=1;j<=m;j++)
					scanf("%d",&dis[i][j][p]);
		int sum=0;
		for(p=1;p<=k;p++)
		{
			tot1=amount1[p]; tot2=amount2[p];
			if(tot1>tot2) break;
			for(i=1;i<=tot1;i++)
			{
				for(j=1;j<=tot2;j++)
				{
					g[i][j]=-dis[loc1[p][i]][loc2[p][j]][p];
				}
			}
			tem=-solve();
			if(tem==inf) break;
			sum+=tem;
		}
		if(p<=k) printf("-1\n");
		else printf("%d\n",sum);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值