【NOI1999】钉子和小球(BSOI1477)

【NOI1999】钉子和小球

Description

  有一个三角形木板,竖直立放,上面钉着n(n+1)/2颗钉子,还有(n+1)个格子(当n=5时如图1)。每颗钉子和周围的钉子的距离都等于d,每个格子的宽度也都等于d,且除了最左端和最右端的格子外每个格子都正对着最下面一排钉子的间隙。
  让一个直径略小于d的小球中心正对着最上面的钉子在板上自由滚落,小球每碰到一个钉子都可能落向左边或右边(概率各1/2),且球的中心还会正对着下一颗将要碰上的钉子。例如图2就是小球一条可能的路径。
  我们知道小球落在第i个格子中的概率pi=,其中i为格子的编号,从左至右依次为0,1,...,n。
  现在的问题是计算拔掉某些钉子后,小球落在编号为m的格子中的概率pm。假定最下面一排钉子不会被拔掉。例如图3是某些钉子被拔掉后小球一条可能的路径。
    
               图1                            图2                           图3

Input

  第1行为整数n(2<=n<=50)和m(0<=m<=n)。以下n行依次为木板上从上至下n行钉子的信息,每行中‘*’表示钉子还在,‘.’表示钉子被拔去,注意在这n行中空格符可能出现在任何位置。

Output

  仅一行,是一个既约分数(0写成0/1),为小球落在编号为m的格子中的概pm。既约分数的定义:A/B是既约分数,当且仅当A、B为正整数且A和B没有大于1的公因子。

Sample Input

5 2
*
* .
* * *
* . * *
* * * * *

Sample Output

7/16

Solution

非常有趣的一道题,可以看作数学期望的基础题。如果有钉子,那么接下来的两个点的概率均为上一点的1/2.如果没有,那么下一点的概率和上一点相同。递推即可。

CODE

#include<iostream>
using namespace std;
long long n,m,f[55][55],tot;
char map[55][55];
int main() {
	int i,j,k;
	cin>>n>>m;tot=1ll<<n;
	for(i=1;i<=n;i++)for(j=1;j<=i;j++)cin>>map[i][j];
	f[1][1]=tot;
	for(i=1;i<=n;i++)
		for(j=1;j<=i;j++)
			if(map[i][j]=='*')
				f[i+1][j]+=f[i][j]/2,f[i+1][j+1]+=f[i][j]/2;
			else f[i+2][j+1]+=f[i][j];
	if(f[n+1][m+1]==0){cout<<0<<'/'<<1;return 0;}
	while(f[n+1][m+1]%2==0){f[n+1][m+1]/=2;tot/=2;}
	cout<<f[n+1][m+1]<<'/'<<tot;
	return 0;
}



### 问题解析 NOI1999 生日蛋糕问题要求制作一个体积为 $ N\pi $、层数为 $ M $ 的多层蛋糕,每层是一个圆柱体,且满足以下约束条件: - 每一层的半径高度都必须严格小于其下一层。 - 蛋糕的体积固定,表面积(除最下层底面外)最小化。 此问题是一个典型的搜索问题,结合了剪枝优化策略,以减少不必要的状态搜索。由于 $ M \leq 20 $、$ N \leq 10000 $,暴力枚举所有可能的 $ R_i $ $ H_i $ 组合效率极低,因此采用**深度优先搜索(DFS)+剪枝**策略是合理的。 ### 解法思路 #### 1. 搜索策略 采用深度优先搜索(DFS),从最下层蛋糕开始向上构造。每一步尝试枚举当前层的半径 $ R $ 高度 $ H $,并递归构造上一层蛋糕,直到构造完 $ M $ 层。 #### 2. 剪枝策略 为了提高效率,必须引入剪枝策略: - **可行性剪枝**:在构造当前层时,若剩余体积不足以构造剩余层数的蛋糕,则剪枝。 - **最优性剪枝**:若当前累计表面积已经超过已有最优解,则剪枝。 - **预处理最小体积最小表面积**:提前计算第 $ i $ 层到第 $ M $ 层的最小可能体积 $ V_{min}[i] $ 最小可能表面积 $ S_{min}[i] $,用于剪枝。 #### 3. 状态表示 使用递归函数 `dfs(u, r, h, v, s)` 表示当前构造到第 $ u $ 层,该层的半径为 $ r $,高度为 $ h $,当前累计体积为 $ v $,表面积为 $ s $。目标是构造完 $ M $ 层后体积等于 $ N $,并使 $ s $ 最小。 #### 4. 数学公式 - 体积公式:$ V_i = R_i^2 \cdot H_i $ - 表面积公式(不包括最下层底面): - 上表面:$ \pi R_1^2 $ - 侧面积:$ 2\pi R_i \cdot H_i $,从第1层到第 $ M $ 层累加 ### 代码实现 ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 25; int N, M; int min_volume[MAXN], min_surface[MAXN]; int ans = INT_MAX; void dfs(int depth, int r, int h, int volume, int surface) { if (depth == M + 1) { if (volume == N) ans = min(ans, surface); return; } // 剪枝:如果当前体积已经超过总体积,或即使加上最小体积也无法完成 if (volume + min_volume[M - depth + 1] > N) return; if (surface + min_surface[M - depth + 1] >= ans) return; // 枚举当前层的半径高度 for (int R = r - 1; R >= M - depth + 1; --R) { for (int H = h - 1; H >= M - depth + 1; ++H) { int V = R * R * H; int S = 2 * R * H; if (volume + V > N) continue; if (depth == 1) dfs(depth + 1, R, H, volume + V, surface + 2 * R * H + R * R); else dfs(depth + 1, R, H, volume + V, surface + 2 * R * H); } } } int main() { // 预处理最小体积最小表面积 for (int i = 1; i < MAXN; ++i) { min_volume[i] = min_volume[i - 1] + i * i * i; min_surface[i] = min_surface[i - 1] + 2 * i * i; } cin >> N >> M; dfs(1, N, N, 0, 0); cout << ans << endl; return 0; } ``` ### 关键点说明 - **预处理**:提前计算第 $ i $ 层到第 $ M $ 层的最小体积表面积,便于剪枝[^1]。 - **剪枝优化**:通过判断当前体积是否可能达到目标体积,以及当前表面积是否可能优于已有解来减少搜索空间[^2]。 - **递归构造**:每次递归构造上一层蛋糕,并更新当前体积表面积。 ### 时间复杂度分析 由于采用了剪枝策略,实际运行时间远低于理论上限。最坏情况下,时间复杂度约为 $ O(M^4) $,但大多数测试用例中表现良好。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值