poj3343 Against Mammoths,二分图匹配

poj3343 二分图匹配
一道有(hen)趣(shui)的题目。
二分时间上界,可以将最优化问题变成判定性问题。
然后用二分图匹配判断是否每个星球都被匹配上就可以啦。

有几个trick:1.时间上界可能达到1600080000左右。
2.当人类星球初始值是0时,至少要1年时才能出发。
3.如果人类某星是0,0 ,外星人某星是0,0 也是不合法地。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define Maxn 261
#define INF 1800000010

int link[Maxn],vis[Maxn],d[Maxn][Maxn],g[Maxn][Maxn];

int fi[Maxn],ne[Maxn*Maxn],to[Maxn*Maxn],te;

int ia[Maxn],pa[Maxn],ih[Maxn],ph[Maxn];
int H,A;

void addedge(int u,int v){
    te++;ne[te]=fi[u];fi[u]=te;to[te]=v;
}

bool find(int i){
	int j,e;
	for(e=fi[i];~e;e=ne[e]){
        j=to[e];
        if (!vis[j]){
            vis[j]=1;
			if (link[j]==0 || find(link[j])){
				link[j]=i;
                return true;
			}
		}
	}
	return false;
}

int cal(int i,int j){
    if (ih[i]==0&&ph[i]==0) return INF;
    int tmp=ia[j]-ih[i]+d[i][j]*pa[j];
    //printf(" %d %d %d\n",i,j,tmp);
    if (ih[i]==0&&tmp==0) return 1+g[i][j];
    if (tmp<=0) return g[i][j];
    int ret=0;
    int tp=ph[i]-pa[j];
    if (tp<=0) return INF;
    if (tmp%tp){ret++;}
    ret+=tmp/tp;
    ret+=d[i][j];
    return ret;
}

int check(int m){
    int i,j;
    memset(fi,-1,sizeof(fi));
    te=0;
    for(i=1;i<=H;++i){
        for(j=1;j<=A;++j){
            if (g[i][j]<=m) {
                addedge(i,j);
            }
        }
    }

    memset(link,0,sizeof(link));
	int ans=0;
	for(i=1;i<=H;i++){
        memset(vis,0,sizeof(vis));
		if (find(i)) ans++;
    }
	if (ans==A) return 1;
	else return 0;
}


int main(){
    //freopen("3343in.txt","r",stdin);
    int i,b,j;
	while(1){
		scanf("%d%d",&H,&A);
		if (H==0&&A==0) break;
		for(i=1;i<=H;++i){
            scanf("%d%d",&ih[i],&ph[i]);
        }
        for(i=1;i<=A;++i){
            scanf("%d%d",&ia[i],&pa[i]);
        }


      	for(i=1;i<=H;i++)
            for(j=1;j<=A;++j){
                scanf("%d",&b);
                d[i][j]=b;
            }

        for(i=1;i<=H;++i){
            for(j=1;j<=A;++j){
                g[i][j]=cal(i,j);
                //printf("%d ",g[i][j]);
            }
            //printf("\n");
        }

        int l=0,r=1700011000,m,ans=r+10;
        while(l<=r){
            //printf("%d %d\n",l,r);
            m=((long long)l+r)/2;
            if (check(m)) {
                if (ans>m) ans=m;
                r=m-1;
            }
            else l=m+1;
        }
        if (ans>1700011000) printf("IMPOSSIBLE\n");
        else printf("%d\n",ans);
    }
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值