CodeForces - 294B Shaass and Bookshelf

本文探讨了如何将不同厚度的书放置在书架上以使书本与书架底面接触的边厚度最小的问题。提出了两种解决方案:一是通过枚举宽度最小的书籍组合,二是使用动态规划方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题意:N本书放到书架上。每本书有厚度t(1或2)和宽度w。问怎么放书才能让书和书架底面接触的边 厚度最小(示意图见原题)。

思路:一开始是想贪心做的,假设每本书厚度相同,我们一定首先把宽度小的放在上面。所以 先把所有书放在下面,然后把书按宽度/厚度 比排序,之后优先把比值小的放在上面,结果会wa;后来发现是因为不能简单通过比例确定不同厚度书之间的先后关系。如:

4

1 2

2 11

2 11

2 5

1 2优先放在上面,结果是6 ,但是正解应该是把2 5放上去之后的5。

正解:因为正确答案一定是分别从厚度为1和2的书中取了几本宽度最小的,把他们放到上面,让答案最小。所以我们把每本书按照宽度从小到大排序,宽度为1的序列取前i个最优的,宽度为2的序列取前j个最优的,通过枚举i,j找到最优解。

如果有O(n)出结果的方法请dalao指教。一开始贪心贪错了。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
typedef vector<int> vi;
#define de(x) cout << #x << "=" << x << endl
#define rep(i,a,b) for(int i=a;i<(b);++i)
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define lb(x) (x&-(x))
const int N = 101010;
struct book{
	int t;
	int w;
} a[105],b[105],asum[105],bsum[105];
int n,tmpt,tmpw,anum=0,bnum=0; 
int cmp(struct book a,struct book b)
{
	return a.w<b.w;
}
int main(){
	cin>>n;
	rep(i,0,n) {
		scanf("%d%d",&tmpt,&tmpw);
		if(tmpt==1) 
		{
			a[anum+1].t=tmpt;
			a[(anum++)+1].w=tmpw;
		}
		else
		{
			b[bnum+1].t=tmpt;
			b[(bnum++)+1].w=tmpw;
		}
	} 
	sort(a+1,a+anum+1,cmp);
	sort(b+1,b+bnum+1,cmp);
	rep(i,1,anum+1) {asum[i].t=a[i].t+asum[i-1].t; asum[i].w=a[i].w+asum[i-1].w;}
	rep(i,1,bnum+1) {bsum[i].t=b[i].t+bsum[i-1].t; bsum[i].w=b[i].w+bsum[i-1].w;}
        int ans=999999;//允许的最小宽度 是否能把最上面的N个放上去 
	
	rep(i,0,anum+1) rep(j,0,bnum+1)
	{
		if(asum[i].w+bsum[j].w<=asum[anum].t-asum[i].t+bsum[bnum].t-bsum[j].t) 
		{//上面的总长度<=下面的宽度和
			if(ans>asum[anum].t-asum[i].t+bsum[bnum].t-bsum[j].t) 
			    ans=asum[anum].t-asum[i].t+bsum[bnum].t-bsum[j].t;
		}
	}
	printf("%d\n",ans);
    return 0;
}


思路2: DP dp[i][j][k] 表示前i个数是否能构成下面长j,上面长k的情况。

转移方程:

dp[i][j][k]=(j-a[i].t>=0&&dp[i-1][j-a[i].t][k]);
dp[i][j][k]=(k-a[i].t>=0&&dp[i-1][j][k-a[i]]);

找到所有状态【这里不用保证状态合法,只要最后合法,过程中上面的大于下面的也可以】,找到i=n时最小合法的j就是答案。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
typedef vector<int> vi;
#define de(x) cout << #x << "=" << x << endl
#define rep(i,a,b) for(int i=a;i<(b);++i)
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define lb(x) (x&-(x))
const int N = 101010;
struct book{
	int t;
	int w;
} a[105];
int dp[105][205][205]; //前i本书 下面长为j 上面长为k的情况是否存在 
//状态转移方程:
//rep(i,1,n+1) rep(j,1,2*n) rep(k,1,2*n)
//dp[i][j][k]=(j-a[i].t>=0&&dp[i-1][j-a[i].t][k]); 
//dp[i][j][k]=(k-a[i].t>=0&&dp[i-1][j][k-a[i]]); 
int main(){
	int n;
	cin>>n;
	rep(i,1,n+1) 	scanf("%d%d",&a[i].t,&a[i].w);
	dp[0][0][0]=1;
	rep(i,1,n+1) rep(j,0,2*n+1) rep(k,0,2*n+1)
	{
	if(j-a[i].t>=0&&dp[i-1][j-a[i].t][k]) dp[i][j][k]=1; 
	if(k-a[i].w>=0&&dp[i-1][j][k-a[i].w]) dp[i][j][k]=1; 	
	}
	int flag=0;
	
	rep(i,0,2*n+1) 
	{
	rep(j,0,2*n+1)
	{
		if(dp[n][i][j]>0&&i>=j)
		{
			printf("%d\n",i);
			flag=1;
			break;			
		}
	}	if(flag) break;
	}
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值