NKOI 3080 打包

(pack.cpp/in/out) 1S/128M

N圆柱形的盒子,底面积均相同,高度不同盒子的高度都是2的非负次幂,即2^i(i= 0, 1, 2, …)每个盒子有一个价值

有若干个集装箱用来打包这N个盒子。集箱也是圆柱形的,底面积稍比盒子大一点,也就是说,盒子在集装箱里只能叠放,不能并列放。集箱的高度也是2的非负次幂,即2^i(i= 0, 1, 2, …)

个集装箱必须满盒子,才能安全运输。例如5个盒子,高度价值分别是(第1个整数是高度的指数2个整数是价值):

1 3

1 2

3 5

2 1

1 4

2个集装箱,高度分别是2^12^2那么3种方案2个盒子装2装箱分别总价值为345。有1种方案用3个盒子装2个集装箱,得到总价值为9

是这些盒子却无法装满一个高度为2^5集装箱。

出盒子和集装箱的信息,求装满所有集装箱的总价值最小的方案。

输入格式:

1行:1整数N(1 <= N <= 10000)表示盒子的数量

下来N行,每行2个整数,分别表示盒子高度的指数价值高度不超过1000价值不超过10000

下来1行,1个整数Q表示装箱类型数

下来Q行,每行2个整数,1个表示这种集装箱的高度(),第2表示这种集装箱的数量

装箱总数不超过5000每个集装箱的高度()超过1000

 

输出格式:

如果所给盒子能装满全部集装箱,输出最小的总价值。如果能,输出-1

 

输入样例

5

1 3

1 2

3 5

2 1

1 4

2

1 1

2 1

 

输出样例

3


注意到这里的高度可以直接用指数来表示,然后就考虑判定是否有解的情况

对于高度为0的集装箱,所有高度为0的盒子的个数必定要大于集装箱的个数,也就是说如果高度为0的盒子的个数小于高度为0的集装箱的个数其实是无解的

由于题目只要求装满集装箱,并不关心每个集装箱里装的是什么样的盒子,我们用cnth[i]记录高度为i个盒子个数,cntx[i]记录高度为i的集装箱个数

cnth[i]除了题目中给出的高度为i的盒子总数,我们还可以考虑将两个高度为i-1的盒子合成一个高度为i的盒子,每找到这样一对cnth[i]++

所以对于每一个高度都应该先计算下其cnth[i]然后判断cnth[i]与cntx[i]的大小关系

由于要求找到最优解,我们只需在判断是否有解的时候顺便更新答案,具体步骤如下:

1.第k步时,若非无解情况,则选前cntx[k-1]个价值最小的盒子;

2.第k步时,剩下cnth[k-1]-cntx[k-1]个盒子,则把它从小到大排序后,让第2*I-1与2*I(I>=1个盒子结合成一个尺寸为k的大盒子。

很好看出,这就是一个贪心原则

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=20005;
inline void _read(int &x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
struct wk{
    int h,t;
    bool operator<(const wk& u)const{
    	return h<u.h;
	}
}s[maxn];
int n,Q,cnth[maxn],cur,v[maxn],ans,tot,last[maxn],Next[maxn];
void insert(int Size,int val){//这里用链表保存不同高度的盒子
	cnth[Size]++,v[++tot]=val;//注意只有相同高度的盒子才在同一条链上
	Next[tot]=last[Size];
	last[Size]=tot;
	for(int i=last[Size];i&&Next[i];i=Next[i])
	    if(v[i]>v[Next[i]])swap(v[i],v[Next[i]]);
}
void update(int Size){
	while(cur<Size){
		while(cnth[cur]>=2){
			cnth[cur]-=2;
			int p=last[cur];
			insert(cur+1,v[p]+v[Next[p]]);
			last[cur]=Next[Next[p]];
		} 
		cur++;
	}
}
void solve(){
	for(int i=1;i<=Q;i++){
		while(s[i].t>0){
			int temp=s[i].h;
			if(cur<temp)update(temp);
			if(cnth[cur]>0){
				int p=last[temp];
				ans+=v[p];
				last[temp]=Next[p];
				cnth[cur]--;
				s[i].t--;
			}
			else{
				puts("-1");
				return ;
			}
		}
	}
	cout<<ans;
}
int main(){
	_read(n);
	int i,j,x,y;
	for(i=1;i<=n;i++){
		_read(x);_read(y);
		insert(x,y);
	}
	_read(Q);
	for(i=1;i<=Q;i++){
		_read(s[i].h);_read(s[i].t);
	}
	sort(s+1,s+1+Q);
	solve();
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值