题解 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 提供的代码与细节的分析,受益匪浅。
萍水相逢,祝前途顺遂!!!

浙公网安备 33010602011771号