HDU 2089 不要62
限制:
62 不能連號, 不能出現4
思路:
1. 對於當前位數字是 4 有限制,可以直接處理!
2.
pre_num(前一個數字) 有限制;
if(pre_num == 6) dp當前位不能取 2 狀態變化.
else dp數組可以任意取.
所以對於當前位 的 前面位有兩種情況,
但是我們是寫的DFS,所以先對於後面的狀態先更新,所以dp更新的時候對於後一位要記錄dp[][0/1] (0/1代表 pre_num(=6 / !=6 ))
int dp[10][2], a[10];
int DFS(int pos, int sta, bool limit){
if(pos < 0) return 1;
if(!limit && dp[pos][sta] != -1) return dp[pos][sta];
int up = limit ? a[pos] : 9;
int temp = 0;
for(int i=0;i<=up;i++){
if(i == 4) continue;
if(sta && i == 2) continue;
temp = temp + DFS(pos-1, i==6, limit && (i == up));
}
if(!limit) dp[pos][sta] = temp;
return temp;
}
int solve(int n){
int num = 0;
while(n){
a[num++] = n % 10;
n /= 10;
}
return DFS(num-1, 0, true);
}
int main(){
int n, m;
memset(dp, -1, sizeof(dp));
while(scanf("%d%d", &n, &m)){
if(!n && !m) break;
printf("%d\n", solve(m) - solve(n-1));
}
return 0;
}
HDU 4734 F(x);
在區間[0, B]內有多少個 F(x) <= F(A).
F(x) = An * 2n-1 + An-1 * 2n-2 + … + A2 * 2 + A1 * 1.
限制:
1. 先考慮F(x) 的最大值,最高(2^9 - 1) * 9 = 525 * 9
數位DP(大)都是從高位搜下去的,如果按照題意考慮我們持續從最高位搞箇中間值加起來!
dp可以記錄,當前位是多大的 個數。
但是如何轉移呢???對於之前那位,OK,我已經知道之前的和 pre_sum 是多少,
現在我們只要算一下當前pos 滿足 F(A) - pre_sum >= 0的那些 DP 值.
考慮減法;
如果對於當前值就是F(A),那麼之後的權值和是不是<=F(A)就行了,
是不是變成了當前值是temp,那麼之後的權值和是不是<=temp就行了,
那我直接把temp直接記錄,就是對於當前位 <= temp 的數量.
注意:
存數位的數組一些位置無意義但被使用,要初始化!
int dp[11][5000];
int a[11];
int n, m;
int DFS(int pos, int sta, bool limit){
if(sta < 0) return 0;
if(pos < 0) return 1;
if(!limit && dp[pos][sta] != -1) return dp[pos][sta];
int up = limit ? a[pos] : 9;
int ans = 0;
for(int i=0;i<=up;i++)
ans = ans + DFS(pos-1, sta-(1<<pos)*i, limit && (i == up));
if(!limit) dp[pos][sta] = ans;
return ans;
}
int solve(){
int num = 0;
int temp = 0;
while(n){
temp = temp + (n % 10) * (1<<num);
n /= 10;
num++;
}
memset(a, 0, sizeof(a));
num = 0;
while(m)
{
a[num++] = m % 10;
m /= 10;
}
return DFS(num-1, temp, true);
}
int main(){
int T, cas=1;
scanf("%d", &T);
memset(dp, -1, sizeof(dp));
while(T--){
scanf("%d%d", &n, &m);
printf("Case #%d: %d\n", cas++, solve());
}
return 0;
}
51nod1009 數字1的數量
1-N的數的1的數量;
數位上的思考:
1. 個位上1的數量,不考慮個位限制,也就是意味着區間[1, 9],num0 = 1;
2. 加一個十位 1的數量,不考慮個/十位限制,也就是意味着區間[1, 99],
num1 = (兩位數, 1 * 10(十位爲1,個位隨意[0, 9]) + 1 * 9(個位爲1, 十位(1, 9))) + 1(個位數, num0 = 1)
3. 再加一個百位 1的數量,不考慮個/十/百位限制,也就是意味着區間[1, 999]
num2 = (三位數, 1 * 100(百位爲1,其餘隨意[0, 99] + 1 * 90(十位爲1, 個位[0, 9], 百位[1, 9]))) + (兩位數, num1)
4. 現在直接改稱 <= 4 位的一個數[1, 9999],
num4 = 1 * 1000 + 1 * 9 * 100 + 1 * 9 * 100 + 1 * 9 * 100 + num2 = 1*1000 + 3*900 + num2.
顯然這還是不夠的,
我們還要考慮,
like 區間[1, 36], 顯然[1, 9],我們現在要知道的是[10, 36], 但是3>=1, 還是沒有限制
like 區間[1, 106], 其實顯然[1, 99]我們都知道了, 我們現在要知道的是[100, 106], 百位==1, 總共有6個, 十位只能是0, 個位1的時候,只能是百位爲1十位是0
like 區間[1, 236], 其實顯然[1, 99]我們都知道了, 我們現在要知道的是[100, 236], 百位==1, 總共有37[0, 36], 個位爲1,百位可以 2 1/2, 個位可以[0, 9]
like 區間[1, 216], 其實顯然[1, 99]我們都知道了, 我們現在要知道的是[100, 216], 百位==1, 總共有17[0, 16], 十位爲1,百位爲1, 個位可以10[0, 9], 但是百位爲1, 個位只能是7[0, 6]
like 區間[1, 2345], 其實顯然[1, 999]我們都知道了, 我們現在要知道[1000, 2345], 千位==1, 總共有346[0, 345],
百位爲 1, 千位可以1/2, 後面就是 46 [0, 45]
總結一下???
對於這裏我們可以考慮特殊情況然後暴力可以得到。
感覺方法太爛!!!
考慮遞推,我們從低到高處理,
答案是累加的過程。
like 區間[1, 36]至36, 對於最高位是1, 都OK就是加[0, 9] 然後考慮尾數1的個數(統籌考慮), 那麼就是 +=num1*3([0,1,2]), 之前的就是高位爲3的情況
like 區間[1, 106]至106, 對於最高位是1, 那麼就是 +=7[0, 06](+尾數+1), 然後加上num2*1(最高位取0)
like 區間[1, 236]至236, 對於最高位是1, 都OK就是加[0, 99], 然後考慮尾數1的個數, 那麼就是 +=num2*3([0,1,2]) + 之前的就是高位爲3的情況
OK!
也可以考慮DFS,考慮每一位的數的貢獻。
CodeForces55D Beautiful numbers
題意:
查詢區間有多少個 整除 自己每位上非0的數
限制:
0無限制、能整除每一位。
X % Y == 0
X * 10 % Y == 0
方案1.[GG]
一開始開了臨時值然後直接dp[pos]巨撒比,明明這一位對於前一位是有特殊關係的,然後直接後一位求出來了,對於前一位的另一個都不滿足了
like -> if(!limit && dp[pos] != -1) return dp[pos];
死於–>轉移
方案2.[GG]
想了開狀態來記錄 [至當前] 能 (%x == 0) 的方案數,但是沒用啊!!!
但是中間寫崩了————對於中間的時候,很撒比地 如果 [(中間值*10 + i % i) != 0] 直接不管了!!
woc??? 你怎麼知道的??? 對於中間的時候 雖然現在不能模i等於0,不代表之後加一些數可以實現膜i等於0吧
死於—>轉移
總結一下————
1.顯然我們是需要中間值的,用來轉移
等等!這個中間好大啊!怎麼優化一下,%(123456789的LCM) 就好了嘛.
2.我們無法在中間判斷這個值是不是不合法!!!NO WAY!!!所以我們還要記錄到個位的時候是不是對所有的位都能膜他等於0
Sooooooooo!!!我們是不是要記錄前面這些位,怎麼記錄————乘起來很棒棒!!!————LCM更加棒棒!!!
直接存 空間複雜度好像很大!!!我們考慮LCM有多少個?(話說怎麼求啊)這些LCM (1*2*3*…*8*9) 的約數嘛, 我們可以離散化一下
方案3.
現在我們數位DP, 每次會記錄一箇中間值temp,一個lcm, 不滿足的話 就是到了最低位 temp % lcm != 0;
那麼我們開 dp[][][]三維就可以剛了!
int mod, hs[3000], xs[55];
void init(){
mod = 2520;
int num = 0;
for(int i=1;i<=mod;i++)
if(mod % i == 0){
xs[++num] = i;
hs[i] = num;
}
// printf("%d\n", num);
}
int a[25];
LL dp[25][3000][55];
int LCM(int x, int y){
return x * y / __gcd(x, y);
}
LL DFS(int pos, int temp, int lcm, bool limit){
if(pos < 0){
if(temp % lcm == 0) return 1;
return 0;
}
if(!limit && dp[pos][temp][hs[lcm]] != -1) return dp[pos][temp][hs[lcm]];
int up = limit ? a[pos] : 9;
int tmp, tlcm;
LL ans = 0;
for(int i=0;i<=up;i++){
tmp = (temp * 10 + i) % mod;
if(i){
tlcm = LCM(lcm, i);
ans = ans + DFS(pos-1, tmp, tlcm, limit && (i == up));
}
else ans = ans + DFS(pos-1, tmp, lcm, limit && (i == up));
}
if(!limit) dp[pos][temp][hs[lcm]] = ans;
return ans;
}
LL solve(LL n){
int num = 0;
mem(a, 0);
while(n){
a[num++] = n % 10;
n /= 10;
}
return DFS(num-1, 0, 1, true);
}
int main(){
int T;
LL l, r;
init();
mem(dp, -1);
scanf("%d", &T);
while(T--){
scanf("%lld%lld",&l, &r);
printf("%lld\n", solve(r) - solve(l-1));
}
return 0;
}
HDU3555 Bomb
題意:
1-N到有多少個數是包含”49”的.
思路:
還記得不要62嘛…
emmmmm, 我們算一下不要49…然後哼哼哼…
LL dp[25][2];
int a[25];
LL DFS(int pos, int sta, bool limit){
if(pos < 0) return 1;
if(!limit && dp[pos][sta] != -1) return dp[pos][sta];
int up = limit ? a[pos] : 9;
LL ans = 0, t;
for(int i=0;i<=up;i++){
if(sta && i == 9) continue;
if(i == 4) t = 1;
else t = 0;
ans = ans + DFS(pos-1, t, limit && (i == up));
}
if(!limit) dp[pos][sta] = ans;
return ans;
}
LL solve(LL n){
int num = 0;
mem(a, 0);
while(n){
a[num++] = n % 10;
n /= 10;
}
return DFS(num-1, 0, true);
}
int main(){
int T;
LL n;
mem(dp, -1);
scanf("%d", &T);
while(T--){
scanf("%lld", &n);
printf("%lld\n", n - solve(n) + 1);
}
return 0;
}
LightOJ 1140
題意:
給出一個區間,求區間內0出現的個數。
思路:
因爲窩比較蠢,在想這個特殊位不就是0嘛,ok,狀態設個 是0/不是0,感覺美滋滋。
但是顯然,這就有一個重大的問題,是的沒錯,對於當前位,123456789這些數字,他的後多少位出現0的個數都沒區別,
但是數位DP,這個DP記錄的值表示的是對於當前位的前面所有情況!!!所以太明顯了這個思路錯的!
所以DP的狀態 需要的是到當前位的一類情況。
那麼對於這道題,記錄從首位到當前位零的個數就對了。
LL dp[25][15];
int a[25];
LL DFS(int pos, bool pre_zero, bool limit, int sta){
if(pos < 0) return (pre_zero ? 1 : (LL)sta);
if(!limit && !pre_zero && dp[pos][sta]!=-1) return dp[pos][sta];
int up = limit ? a[pos] : 9;
LL temp = 0;
for(int i=0;i<=up;i++){
if(pre_zero){
temp += DFS(pos-1, pre_zero&&i==0, limit&&i==up, sta);
}
else{
if(!i) temp += DFS(pos-1, pre_zero, limit && i==up, sta+1);
else temp += DFS(pos-1, pre_zero, limit && i==up, sta);
}
}
if(!limit && !pre_zero) dp[pos][sta] = temp;
return temp;
}
LL solve(LL x){
if(x<0) return 0;
if(x==0) return 1;
int num = 0;
while(x){
a[num++] = x % 10;
x /= 10;
}
return DFS(num-1, true, true, 0);
}
int main(){
LL n, m;
int T, cas = 1;
memset(dp, -1, sizeof(dp));
scanf("%d", &T);
while(T--){
scanf("%lld%lld", &n, &m);
printf("Case %d: ", cas++);
printf("%lld\n", solve(m) - solve(n-1));
}
return 0;
}