P5261数字理论题解
题目链接
找数字,一眼数位dp。
考虑设计状态 \(f(k, s, x, p)\),表示当前在第 \(k\) 位,数字之和为 \(s\),这一位给下一位进位为 \(x\),乘 \(D\) 后和为 \(p\)。
转移很显然:
\[f(i+1, s+\Delta cur, \lfloor \frac {x + \Delta cur*D}{10}\rfloor,p+(x + \Delta cur*D) \mod 10 ) = f(i+1, s+\Delta cur, \lfloor \frac {x + \Delta cur*D}{10}\rfloor, p+(x + \Delta cur*D) \mod 10 ) or f(i, s, x, p)
\]
这个方程可以考虑用bitset优化,因为如果前三个状态相同,对于 \(p\) 是统一进行加操作的。故这里可以通过左移来实现转移。
我们从低位向高位枚举,即可得到每一种方案是否合法。
对于统计答案——
因为要求最小的,故应从高位到低位,每一位都从小到大枚举,判断是否合法。注意,由于乘 \(D\) 后可能变成 \(K+1\) 位数,故对于最高位,其 \(p\) 值不一定为 \(P\)(还可能是 \(P-x\))。
Code:
#include<bits/stdc++.h>
using namespace std;
int K, S, P, D;
int ans[105];
bitset<1023> f[102][1023][10];//P K S 进位
int main(){
scanf("%d%d%d%d", &K, &S, &P, &D);
f[0][0][0][0] = 1;
for(int i = 0; i<K; i++){
for(int j = 0;j<=i*9; j++){
for(int k = 0; k<=9; k++){
for(int cur = (i==(K-1)); cur<=9; cur++){
int sum = cur*D+k;
f[i+1][cur+j][sum/10]|=(f[i][j][k]<<(sum%10));
}
}
}
}
int x, ts, tp, tsum;
int tmps = S, tmpp = P;
bool flag = 0;
for(int i = 0; i<=9; i++){
if(P>=i&&f[K][S][i][P-i]){//注意,进位后可能为K+1位数xwx
x = i;
flag = 1;
tmpp-=i;
break;
}
}
if(!flag){
puts("-1");
}
else{
for(int i = K; i>=1; i--){
bool flag2 = 0;
for(int cur = (i==K); cur<=9; cur++){
for(int px = 0; px<=9; px++){
tsum = px+cur*D;
ts = tmps-cur;
tp = tmpp-(tsum%10);
if(ts<0 || tp < 0 || (tsum/10)!=x || !f[i-1][ts][px][tp]){
continue;
}
tmps = ts;
tmpp = tp;
x = px;
ans[i] = cur;
flag2 = 1;
if(flag2){
break;
}
}
if(flag2){
break;
}
}
}
for(int i = K; i>=1; i--){
printf("%d", ans[i]);
}
}
system("pause");
return 0;
}