【51nod 1392】【最大费用最大流】装盒子

Description

有n个长方形盒子,第i个长度为Li,宽度为Wi,我们需要把他们套放。注意一个盒子只可以套入长和宽分别不小于它的盒子,并且一个盒子里最多只能直接装入另外一个盒子 (但是可以不断嵌套),例如1 * 1 可以套入2 * 1,而2 * 1再套入2 * 2。套入之后盒子占地面积是最外面盒子的占地面积。给定N个盒子大小,求最终最小的总占地面积。

Solution

这道题直接求最小的占地面积很难求,所以我们求被放进盒子的盒子的最大面积。我们将每个点拆成两个点,那么,当一个盒子x的长和宽均不大于另一个盒子y时,我们有左边的x向右边的y连一条容量为1,费用为x面积的有向边。同时源点向左边的每个点连一条容量为1,费用为0的边,右边的每个点向汇点连一条容量为1,费用为0的边,最后跑一遍最大费用最大流即可。
p.s.最大费用最大流怎么跑??
很像最小费用最大流zkw的做法,只要改成spfa跑一下最长路即可。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=405,maxn1=40005;
int a[maxn],b[maxn],first[maxn],last[maxn1],next[maxn1],value[maxn1],cost[maxn1],dui[maxn1];
int n,i,t,j,k,l,x,y,z,num,bz[maxn],d[maxn],v[maxn1],p,ans,fa[maxn],g[maxn];
void lian(int x,int y,int z,int p){
    last[++num]=y;next[num]=first[x];first[x]=num;value[num]=z;cost[num]=p;
}
int pan(){
    memset(d,128,sizeof(d));d[0]=0;
    int i=0,j=1,t,x,k;memset(fa,0,sizeof(fa));
    while (i<j){
        x=v[++i];
        for (t=first[x];t;t=next[t]){
            if (!value[t] || d[x]+cost[t]<=d[last[t]]) continue;
            d[last[t]]=d[x]+cost[t];fa[last[t]]=x;g[last[t]]=t;
            if (!bz[last[t]]) bz[last[t]]=1,v[++j]=last[t];
        }
        bz[x]=0;
    }p++;
    if (d[n*2+1]>0) return 1;return 0;
}
void dg(){
    int x=n*2+1;
    while (x) value[g[x]]--,value[dui[g[x]]]++,x=fa[x];
    ans-=d[n*2+1];
}
int main(){
//  freopen("data.in","r",stdin);
    scanf("%d",&n);
    for (i=1;i<=n;i++)
        scanf("%d%d",&a[i],&b[i]),lian(0,i,1,0),lian(i,0,0,0),lian(i+n,2*n+1,1,0),lian(2*n+1,i+n,0,0),ans+=a[i]*b[i];
    for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)
            if (a[i]<=a[j] && b[i]<=b[j] && i!=j){
                if (a[i]==a[j] && b[i]==b[j] && j<i) continue;
                lian(i,j+n,1,a[i]*b[i]),lian(j+n,i,0,-a[i]*b[i]);
            }
    for (i=1;i<num;i++)
        if (i%2) dui[i]=i+1,dui[i+1]=i;
    while (pan()) dg();
    printf("%d\n",ans);
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值