题解 luogu.P6218 [USACO06NOV] Round Numbers S

题目

luogu.P6218 [USACO06NOV] Round Numbers S

题意建模

DP裸题,进一步熟练思路。

算法分析

声明变量

\(limit\):当前位能否取到最大值(此题中的 \(1\)\(10\) 进制中就是能否取到 \(9\));

\(lead\):当前位是否是前导 \(0\)

\(pos\):当前是从前向后的第 \(pos\) 位。

\(cnt\)\(0\)\(1\) 多多少。由于可能出现负数,统统加上了 \(520\)

\(dp(pos,cnt)\):记忆化数组,记录没有限制且没有前导 \(0\) 时的情况是否搜索过,并记录搜索结果。

具体的算法流程

  • 首先读入数据,转化成二进制存储;

  • 运行DFS函数,从高位逐位往下搜索,遇到特定的情况就缓存答案;

  • 如果当前的状态没有绝对约束,也即是一个完整的局面,并且已然缓存过答案,我们就把答案直接返回;不然,就把答案先缓存,再返回。

  • 考虑搜索的细节,由于是要比较 \(0\)\(1\) 的个数,所以需要维护这两个变量。考虑记录一个变量,首先想到的是 bool 类型的,用于返回当前的搜索分支中是否存在符合条件的解;但是很快就会发现,这样行不通。

  • 为什么?自然是因为两个需要维护的数量是在不断地动态变化的,bool 的功能不足以支撑起维护这些数据,所以想到开一个 int 的变量维护。具体实现结合代码与细节研讨

时间复杂度的分析

复杂度如何保证?当 \(limit\)\(1\) 或者 \(lead\)\(1\) 的时候并没有记忆化,那么如何保证复杂度?为了让 \(limit\)\(1\),每一位都得顶位,只有 \(len\) 种情况;如果让 \(lead\)\(1\),那么每一位都得是 \(1\),也只有 \(len\) 种情况。因此,只有极少数情况未被记忆化,复杂度保障在 \(O(len^{2})\)
的水平。

参考代码

#include<iostream>
#include<cstring>
#define int long long
using namespace std;
const int N=35;
int dp[N][N*9];
int num[N];
int dfs(int pos,int cnt,bool lead,bool limit)
{
	if(!pos) return cnt>=520;
	if(!limit && !lead && dp[pos][cnt]!=-1) return dp[pos][cnt];
	int top=limit?num[pos]:1,ans=0;
	for(int i=0;i<=top;i++) ans+=dfs(pos-1,cnt+(i==0?(lead?0:1):-1),lead&&i==0,limit&&i==top);
	if(!limit && !lead) dp[pos][cnt]=ans;
	return ans;
} 
int solve(int x)
{
	int idx=0;
	memset(dp,-1,sizeof(dp));
	while(x) num[++idx]=x%2,x>>=1;
	return dfs(idx,520,true,true);
}
signed main()
{
	int l,r; cin>>l>>r;
	cout<<solve(r)-solve(l-1)<<endl;
	return 0;
} 

细节实现

细节代码的处理

  • 如果当前所有位搜索完毕,\(0\)\(1\) 多(差值大于 \(0\))返回 \(1\),否则返回 \(0\)
    if(pos==0) return cnt>=30;

  • 如果当前没有特殊限制并且已经搜索过了,直接返回。
    if(!limit&&!lead&&dp[pos][cnt]) return dp[pos][cnt];

  • 当前位能去到的最大值。有限制那么就是原数中的 \(pos\) 位,没有限制也只能取到 \(1\).
    int top=limit?a[pos]:1;

  • 如果之前都有限制且这一位也顶上了位,那么 \(limit\) 也是 \(1\);如果之前都是前导 \(0\) 且这一位还是 \(0\),那么这一位还是前导 \(0\);位置就往下一位搜;计算新差值(前导 \(0\) 统统不算)。
    ans+=dfs(pos-1,cnt+(i==0?(lead?0:1):-1),limit&&i==top, lead&&i==0);

  • 当前不是特殊情况,那么就记忆下来。注意,我们记忆化缓存下来的只有是一个完整而且无后效性,没有绝对约束的状态,否则不值得也不应该记录下来
    if(!limit&&!lead) vis[pos][cha]=1, f[pos][cha]=res;

总结归纳

感谢@__Watcher 提供的代码与细节的分析,受益匪浅。
萍水相逢,祝前途顺遂!!!

posted @ 2025-08-01 15:13  枯骨崖烟  阅读(4)  评论(0)    收藏  举报