成都day2t1

1 、 叠 盒子 ( box .pas/cpp )
【问题描述】
企鹅豆豆家里有很多空盒子,非常占地方。每个盒子的长和宽分别为 Li,Wi,一个盒
子可以放入一个长和宽都不小于它的盒子(可以不断嵌套),注意盒子不能够旋转,Li
有可能小于 Wi。嵌套完后盒子的占地面积是最外面盒子的占地面积之和。豆豆想知道
最终最小的占地面积是多少?
【输入格式】
第一行一个数 N 表示盒子的个数。
接下来 N 行,每行两个正整数,表示每个盒子的长度和宽度。
【输出格式】
一行一个整数表示最终最小的占地面积。
【输入样例】
3
1 1
1 2
2 1
【输出样例】
4
【数据范围】
对于 30%的数据,N≤10;
另外 10%的数据,Li=1;
另外 10%的数据,Li≤2;

对于 100%的数据,1≤N,Li,Wi≤200;

分析

这题看一下数据范围,究极小,可以支持n^3左右的复杂度,显然可以跑网络流之类的算法。

仔细看一下,便有了做题思路,下面给出步骤分:

30%:
阶乘枚举每个盒子被哪个盒子嵌套,或者各种搜索剪枝。
另外 10%:
输出最大数字即可。没分的,去面壁。
另外 10%:
可以 DP 或者贪心。
100%:
最小费用流。每个盒子拆点,中间连接流量为 1 的边,表示盒子只能使用一次。S
向盒子连流量为 1 费用为盒子大小的边。
每个盒子再向 T 连边。做一遍最小费用流就好了。

记得去重

下面给出这一题的标程:

#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define N 405
#define INF 500
//------------------------
struct edge{
int fr,to,flow,cost,next;
}e[N*N*3];int ecnt=1;
int head[N];int S,T;
void addedge(int f,int t,int flow,int cost){
e[++ecnt]={f,t,flow,cost,head[f]};head[f]=ecnt;
e[++ecnt]={t,f,0,-cost,head[t]};head[t]=ecnt;
}
//------------------------
int dis[N];queue<int> que;
int inque[N];
int from[N];
int SPFA(){
memset(dis,0x3f,sizeof(dis));
memset(inque,0,sizeof(inque));
que.push(S);
dis[S]=0;from[S]=0;
while(!que.empty()){
int u=que.front();que.pop();inque[u]=0;
for(int i=head[u];i;i=e[i].next){
if(e[i].flow<=0||dis[e[i].to]<=dis[u]+e[i].cost) continue;
if(!inque[e[i].to]) que.push(e[i].to),inque[e[i].to]=1;
from[e[i].to]=i;
dis[e[i].to]=dis[u]+e[i].cost;
}
}
if(dis[T]==0x3f3f3f3f) return INF*5;
int t=INF,p=from[T];
while(p!=0) t=min(e[p].flow,t),p=from[e[p].fr];
p=from[T];
while(p!=0) e[p].flow-=t,e[p^1].flow+=t,p=from[e[p].fr];
return t*dis[T];
}
int MCMF(int flow){//忘了ZKW怎么写了,不过这种图ZKW会跑死吧 
int cost=0;
int t;
while(t=SPFA()-INF*5){
cost+=t+INF*5;
}
return cost;
}
//-----------------------
int n,ans;
struct th{
int c,k;
bool operator <(const th &b)const{
if(c!=b.c) return c<b.c;
else return k<b.k;
}
}hz[N];
#define cal(i,s) ((i)+200*s)
int main(){
freopen("box.in","r",stdin);
freopen("box.out","w",stdout); 

S=401,T=402;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&hz[i].c,&hz[i].k);
}
sort(hz+1,hz+n+1);
int cnt=0;
for(int i=1;i<=n;i++){
if(hz[i].c==hz[i-1].c&&hz[i].k==hz[i-1].k) continue;
hz[++cnt]=hz[i];
}
n=cnt;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i!=j&&hz[i].c>=hz[j].c&&hz[i].k>=hz[j].k)
addedge(cal(i,0),cal(j,1),INF,0);
}
}
for(int i=1;i<=n;i++){
addedge(S,cal(i,0),1,0);
addedge(cal(i,1),T,1,-hz[i].c*hz[i].k);ans+=hz[i].c*hz[i].k;
}
cout<<ans+MCMF(INF)<<endl;
return 0;

}

///网络流的算法很重要,一定要掌握

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值