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);
}