兹磁伙伴们刷POI
提示:
1. 把这玩意用四进制表示看看
2. 模拟一下退位的过程 , 一次退几位比较合理呢?
详细题解在代码后:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <deque>
#include <stack>
#include <algorithm>
using namespace std;
const int maxn = 2100;
const int INF = 0x3f3f3f3f;
int n;
char s[maxn] , s1[maxn];
deque<int> a;
__inline int dig(char c) { return c-'0'; }
bool div()
{
if(n==0) return false;
int last = 0 , nn = 0 , st = 0;
if(dig(s[0])<4) last = dig(s[0]) , st = 1;
for(int i=st;i<n;i++)
{
int sum = dig(s[i]);
sum+= 10*last;
last = sum%4;
s1[nn++] = sum/4+'0';
}
a.push_front(last);
n = nn;
memcpy(s, s1, sizeof(s1));
return true;
}
const int modu = 1e9;
int d[maxn][2];
int w[maxn][2];
int main(int argc, char *argv[]) {
scanf("%s" , s);
n = strlen(s);
while(div());
a.push_front(0); a.push_back(0);
d[0][0] = 0; w[0][0] = 1;
d[0][1] = INF;
for(int i=1;i<a.size();i++) for(int j=0;j<2;j++)
{
d[i][j] = min(d[i-1][0]+j*5 , d[i-1][1]+j*3);
if(d[i][j]==d[i-1][0]+j*5) w[i][j] += w[i-1][0];
if(d[i][j]==d[i-1][1]+j*3) w[i][j] += w[i-1][1];
d[i][j] += a[i]*(!j?1:-1);
w[i][j] %= modu;
}
printf("%d\n" , w[a.size()-1][0]);
return 0;
}
有几个显而易见的结论:
- 最高位不能高于这个数四进制表示的位数+1
- 如果要退位 , 每次最多退一位 , 也就是把高位上的1 , 拆成4个低位
- 每个位上的数最多只有两种状态 , 这取决于上一位是否退了
d[i][j]处理到第i位,且第i−1位[(j==0)没有,(j==1)有]退位时的答案
w[i][j]此时的方案数
然后就可以开心的DP了 , 转移方程很好写啦……代码浓缩过 , 所以最好自己写写转移方程