洛谷 U88663 紧密数
U88663 紧密数
题目描述
给出一个数字表{0,1,2,---,k},1≤k≤9。可以用这个数字表构造出长度为n的数,如果所构造的这个数的任意两个相邻的数字差都不超过1,则称这个数为该数字表上的n紧凑数。如k=9,101010就是一个长度为6的紧密数。可以认为010101也是一个长度为6的紧密数。
输入格式
测试数据有多组。
每组一行包含两个整数k和n,之间用一个空格隔开。
输出格式
对输入的每一行,输出一个实数,表示长度为n的所有数中,紧密数所占的 百分比,保留小数点后5位。
输入输出样例 #1
输入 #1
4 1
2 5
3 5
8 7
输出 #1
100.00000
40.74074
17.38281
0.10130
说明/提示
1≤n≤100
题意
数字集合:(0,1,2,\dots,k),一共 (k+1) 种数字。
构造长度为 n 的序列(允许前导 0),如果相邻两个数字的差的绝对值 (\le 1),该序列称为紧密数。
求紧密数占全部序列的百分比,结果保留小数点后 5 位输出:
(\text{百分比}= \frac{\text{紧密数总个数}}{\text{总方案数}} \times 100)
总方案数:每个位置有 (k+1) 种选择,(\boldsymbol{total=(k+1)^n})。
数据范围:(1\le n \le 100),直接整数计数会溢出,使用
double浮点 DP。
DP 设计
状态定义
(dp[i][j]):长度为 i,末尾数字为 j 的紧密数数量。
状态转移
dp[i][j]=dp[i-1][0]+dp[i-1][1] &j=0
dp[i-1][k-1]+dp[i-1][k] &j=k
dp[i-1][j-1]+dp[i-1][j]+dp[i-1][j+1] &0<j<k### 初始条件
长度为 1 时所有数字都合法:
(dp[1][j]=1,quad j=0,1,dots,k)
答案计算
dp[n][j]}{(k+1)^n
样例说明
k=4,n=1:长度为 1 全部合法,输出100.00000k=2,n=5:总方案 (3^5=243),紧密数共 99 个;
(99 \div 243 \times100=40.74074)。
代码(c++):
#include <iostream>
#include <iomanip>
#include <cstring>
using namespace std;
const int MAXN=105;
const int MAXK=10;
double dp[MAXN][MAXK];
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int k,n;
// 多组输入直到EOF
while (cin>>k>>n){
memset(dp,0,sizeof(dp));
// 边界:长度为1,所有数字均合法
for(int j=0;j<=k;++j){
dp[1][j]=1.0;
}
// dp转移
for(int i=2;i<=n;++i){
for (int j=0;j<=k;++j){
if(j==0) {
dp[i][j]=dp[i-1][0]+dp[i-1][1];
}else if(j==k){
dp[i][j]=dp[i-1][k-1]+dp[i-1][k];
}else{
dp[i][j]=dp[i-1][j-1]+dp[i-1][j]+dp[i-1][j+1];
}
}
}
double sum=0.0;
for(int j=0;j<=k;++j){
sum+=dp[n][j];
}
// 总方案数 (k+1)^n
double total=1.0;
for (int i=1;i<=n;++i){
total*= (k+1);
}
double ans=sum/total*100.0;
cout<<fixed<<setprecision(5)<<ans<<'\n';
}
return 0;
}

浙公网安备 33010602011771号