2020 ICPC·小米邀请赛 决赛 J. Rikka with Book(状压dp)

文章讨论了如何使用动态规划解决一个关于书籍堆放问题,目标是找到使任意一本书都不掉下的情况下,书堆伸出桌沿的最大长度。通过状态压缩和重心位置分析,提出了一种优化策略和代码实现。

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

题目

登录—专业IT笔试面试备考平台_牛客网

n(n<=20)本书,放在桌子上,

第i本书的可以看成是li(li<=1e3)*1*1的物体,其中长为li,宽为1,高为1,

质量均匀分布,且为wi(wi<=1e3)

求n本书摞在一起,使得任意一本书都不掉下桌子时,书能伸出桌沿的长度的最大值是多少

思路来源

官方题解&申老师

题解

放的话肯定是从上往下放,这样已经放上去的可以看成是一个物体,

并且当b物品摞在a物品之上时,一定是把b物品的重心放到a物品的边沿上,

好比把a物品当成桌子,一定是放到桌沿上,

再将a和b看成同一个物品时,一定是放到下一个物品的边沿上,

一旦一个物体的质量和重心的位置确定了,这个物品的其他属性就无关紧要了,从而无后效性

所以状压每次往下垫的书是哪一本,确定放的顺序,关注的是伸出去的最大值

往下垫的书的重心位于l/2处,质量为a;

上面的书看成一体时,重心位于边沿,质量为b

那么新的重心,根据杠杆原理,位于距边沿a/(a+b)的位置,记add=a/(a+b)

记原来的伸出去的最大值为x,则新的最大值为x+add,

此外,可以旋转一下整个物体,使整个物体的重心仍落在边沿上不落下去,

但是伸出边沿的是往下垫的书的另外半边,也就是l-add这半边,二者取max即可

所以,如果最优解是第i本书伸的最远,最上面的书是1,最下面的书是n,

一定是对于j∈[1,i-1]来说,把[1,j]看成一体时,[1,j]的重心压在j+1的左边沿,

对于j∈[i+1,n]来说,将[1,j-1]看成一体时,[1,j-1]的重心压在j的右边沿

每次枚举的时候,旋转or不旋转二选一都试一下,显然可以覆盖这种情况

代码1

维护的是长度l、到左边沿的距离p、整体的质量w

// Problem: Rikka with Book
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/9328/J?&headNav=acm
// Memory Limit: 1048576 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=20,M=(1<<20)+5;
int n;
db dp[M];
int lb(int x){
	return x&(-x);
}
struct node{
	db l,p;//长度 离左侧距离
	int w;//质量
	node(){l=0;p=1e7;w=0;}
	db dis(){
		return l-p;
	}
	void show(int i=-1){
		printf("i:%d l:%lf p:%lf w:%d\n",i,l,p,w);
	}
}e[M];
//b放在a上
bool operator>(node a,node b){
	return a.dis()>b.dis();
}
node mer(node a,node b){
	db x=a.l-a.p;
	node c;
	db B=a.w*x/(a.w+b.w);
	if(b.p>a.l){//b更左
		c.l=b.l;
		c.p=b.p-B;
		//b.l-b.p+B
	}
	else{//a更左
		c.l=a.l+b.l-b.p;
		c.p=a.l-B;
		//b.l-b.p+B
	}
	c.w=a.w+b.w;
	if(c.p>c.l-c.p)c.p=c.l-c.p;
	//puts("");
	//a.show();b.show();c.show();
	//puts("");
	return c;
}
void sol(){
	sci(n);
	rep(i,0,n-1){
		int x=1<<i;
		scanf("%lf",&e[x].l);
		e[x].p=e[x].l/2.0;
	}
	rep(i,0,n-1){
		int x=1<<i;
		scanf("%d",&e[x].w);
	}
	int up=(1<<n)-1;
	rep(i,1,up){
		if(lb(i)==i)continue;
		//printf("i:%d\n",i);
		rep(j,0,n-1){
			if(!(i>>j&1))continue;
			int v=1<<j,oth=i^v;
			node w=mer(e[v],e[oth]);//只枚举最底下那个是什么
			if(w>e[i])e[i]=w;
			//e[oth].p=e[oth].l-e[oth].p;
			//w=mer(e[v],e[oth]);
			//if(w>e[i])e[i]=w;
			//w.show();
		}
		//if(e[i].p>e[i].l-e[i].p)e[i].p=e[i].l-e[i].p;
		//b[1].p=b[1].l-b[1].p;
		//e[i].show(i);
	}
	printf("%.10lf\n",e[up].dis());
}
int main(){
	sol();
	return 0;
}

代码2(三个顶俩代码)

发现无需维护长度l和距一端的位置p,只维护右半边伸出去的最大值即可

每次尝试一下翻或不翻

#include <bits/stdc++.h>

using namespace std;
int n,l[30],w[30],x[1100000];
double f[1100000];

int main()
{
    int i,j;
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%d",&l[i]);
    for(i=0;i<n;i++)
        scanf("%d",&w[i]);
    for(i=1;i<(1<<n);i++)
    {
    	for(j=0;j<n;j++)
    		if(i&(1<<j))
    			break;
    	x[i]=x[i-(1<<j)]+w[j];
    }
    for(i=1;i<(1<<n);i++)
    {
    	for(j=0;j<n;j++)
    		if(i&(1<<j))
    			f[i]=max(f[i],max(f[i^(1<<j)]+double(0.5*w[j]*l[j])/x[i],l[j]-double(0.5*w[j]*l[j])/x[i]));
    }
    printf("%.12lf\n",f[(1<<n)-1]);
    return 0;
}

<think>嗯,用户想要找2024年ICPC昆明邀请赛的题目解析,但根据提供的引用内容,现有的资料只覆盖到2022年昆明的题目,比如引用[1]提到2020、2021年的ICPC和CCPC题目,以及引用[4]详细解析了2022昆明站的G题。不过用户询问的是2024年的,这可能还没有公开的解析,或者可能还没有举办。需要先确认是否存在2024年的比赛信息,或者是否用户可能记错了年份?比如,2024年的比赛可能尚未举行,或者相关信息还未被整理发布。 首先,检查提供的引用内容,发现最新的昆明站信息是2022年的,引用[4]讨论的是2022昆明G题。用户提到的2024年可能是个错误,或者他们希望提前获取信息。但通常比赛结束后才会有解析,所以需要告知用户目前没有相关资料,并建议他们关注官方渠道或后续的社区分享。同时,可以推荐现有的2022年昆明题解作为参考,帮助用户了解题型和考点,比如动态规划、期望计算等常见问题。 另外,用户可能对如何准备比赛感兴趣,可以建议他们分析历年题目类型,比如引用[1]提到的区域赛常考题型,或者引用[3]中提到的训练经验。需要确保回答准确,避免提供不存在的资源,同时提供有用的替代信息,帮助用户找到最近的可用资料。</think>关于2024年ICPC昆明邀请赛题目解析,目前尚未有官方或主流平台发布完整的题目解析报告。根据历史信息推测,ICPC区域赛的题目解析通常会在赛后由参赛队伍、算法社区或教练团队逐步整理发布。以下是可参考的备赛建议和现有资源: --- ### 一、近期ICPC题型趋势参考 1. **动态规划与组合数学** 如2022年昆明站G题考察了**概率期望与递推关系**,需掌握马尔可夫链、态转移方程的建立[^4] $$E = \sum_{i=1}^n \left( \frac{p_i}{1-p_i} \cdot \sum_{j \neq i} \frac{p_j}{1-p_i} \right)$$ 此类问题常需分析极限态下的数学期望。 2. **数据结构优化** 近年区域赛常出现需要**线段树/树数组维护区间性质**的题目,例如区间最值、历史版本查询等。 3. **图论与网络流** 包括最小割建模、分层图最短路等高级技巧,如2021年沈阳站曾出现网络流与二分答案结合的题目[^2]。 --- ### 二、获取解析的途径建议 1. **官方渠道** 关注ICPC官网及昆明站承办院校的赛事公告,解析可能通过**赛后题解报告会**发布。 2. **算法社区** - **Codeforces**:搜索标签`[ICPC Kunming 2024]` - **知乎/掘金**:技术博主常撰写详细题解(例:2022年G题推导过程) 3. **训练平台** 尝试在**Codeforces Gym**或**牛客竞赛**题库中查找昆明站模拟赛题。 --- ### 三、历届昆明站真题参考 若需练习类似题型,可参考2022年昆明站题目: - **G题(豆子操作期望)**:结合概率论与递推,需推导稳定态下的位置关系 - **B题(几何构造)**:通过坐标系变换简化多边形切割问题 ```python # 示例:概率期望计算的代码框架 def calculate_expected_value(probabilities): n = len(probabilities) expected = 0.0 for i in range(n): pi = probabilities[i] term = pi / (1 - pi) if pi != 1 else 0 sum_other = sum(pj / (1 - pi) for j, pj in enumerate(probabilities) if j != i) expected += term * sum_other return expected ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值