@JintianGo 2015-07-19
16:02 字数 6806 阅读 0KNOW
这道题完完全全是自己做出来的,本以为是数学题,但一看好像数位DP也能做,于是开始脑补,坚持尝试了一个小时终于给攻了出来,很开心很开心哈哈
#include <bits/stdc++.h>using namespace std;#define maxn 40int L, R;int d[maxn][maxn][maxn];int digit[maxn];int dfs(int pos, int num1, int num2, int have, int flag)/// num1 & num2 means before the "pos" the amount of "0" & "1".{if(pos == -1)return num1 >= num2;/// num1 represent the number of 0, and num2 means 1if(!flag && d[pos][num1][num2]!=-1)return d[pos][num1][num2];int ans = 0;int end = flag ? digit[pos] : 1;for(int i=0; i<=end; i++) ///know this value is on "pos" position.{int t1 = num1, t2 = num2, nhave = have;if(have && i==0) t1++;if(i==1){nhave = 1;t2++;}ans += dfs(pos-1, t1, t2, nhave, flag&&i==end);}if(!flag)d[pos][num1][num2] = ans;return ans;}int cal(int x){int pos = 0;if(x == -1)return 0;while(x){digit[pos++] = x % 2;x /= 2;}return dfs(pos-1, 0, 0, 0, 1);}int main(){memset(d, -1, sizeof(d));while(~scanf("%d%d", &L, &R)){printf("%d\n", cal(R) - cal(L-1));}return 0;}
Go
For It
Just Open It
hdu 2089
数位dp/*求[1,n]内有多少个数字,该数字有13,且能被13整除 n<=10^9x % 13 = 0(pre*10^pos + next) % 13 = 0 pre是之前确定的部分需要的参数为pre , pos , 状态havehave记录pre拥有"13",pos+1位为"1",没有"13" 分别用have = 2,1,0表示然后记忆化搜索*/#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int dp[10][13][3];int digit[10];__int64 dfs(int pos , int pre , int have , bool doing){if(pos == -1)return have == 2 && pre == 0;if(!doing && dp[pos][pre][have] != -1)return dp[pos][pre][have];int ans = 0;int end = doing ? digit[pos] : 9;for(int i = 0 ; i <= end ; i ++){int npre = (pre*10 + i) % 13;int nhave = have;if(have == 0 && i == 1)nhave = 1;else if(have == 1 && i != 1)nhave = 0;if(have == 1 && i == 3)nhave = 2;ans += dfs(pos-1 , npre , nhave , doing && i == end );}if(!doing)dp[pos][pre][have] = ans;return ans;}int cal(int x){int pos = 0;while(x){digit[pos++] = x % 10;x /= 10;}return dfs(pos - 1 , 0 , 0 , 1);}int main(){memset(dp,-1,sizeof(dp));int n;while(~scanf("%d",&n))printf("%d\n",cal(n));return 0;}/*问[1,n]内有多少个数字包含"49"这里用记忆化搜索更加好写,而且快hdu 3652 是加强版*/#include<cstdio>#include<algorithm>using namespace std;//有点明白了 这里的dp[i,j]是指当 n ~ //i的前缀已经确定的情况之下,之后的数随意,能够有多少满足条件的数//今天是一个新的起点 我要从数位dp这里来建立自己的自信心//加油 最棒的自己__int64 dp[20][5];int digit[20];__int64 dfs(int pos , int have , bool doing){if(pos == -1)return have == 2;if(!doing && dp[pos][have] !=-1)return dp[pos][have];__int64 ans = 0;int end = doing ? digit[pos] : 9;for(int i = 0 ; i <= end; i++){int nhave = have;if(have == 1 && i != 4)nhave = 0;if(have == 0 && i == 4)nhave = 1;if(have == 1 && i == 9)nhave = 2;ans += dfs(pos-1 , nhave , doing && i == end);}if(!doing){dp[pos][have] = ans;}return ans;}__int64 cal(__int64 x){int pos = 0;while(x){digit[pos++] = x % 10;x /= 10;}return dfs(pos - 1 , 0 , 1);}int main(){memset(dp,-1,sizeof(dp));int T;for(scanf("%d",&T) ; T-- ;){__int64 n;scanf("%I64d",&n);printf("%I64d\n",cal(n));}return 0;}用这种方法写,一个流程是,列出式子(pre*10^pos + next) pre是确定的,next是变量所以参数就为pre,pos加上一些其他的,还有一个标记doing,表示是计算有上界限制的还是没有上界限制(所有情况,即end=9)dfs(pos-1 , npre , doing && i ==end)/**//*题目描述见另外一份平衡,即∑a[i]*(i-o) = 0 o为支点对于一个数,支点o是唯一的,所以不会有重复计数的情况(但有一种特殊的,就是0,00,000等都是一样的,会计算多次,最后减去即可)假设检查到pos处,对于上面的式子∑a[i]*(i-o) = 0,这里确定了支点为o之前的数其∑a[i]*(i-o)的结果为pre所以参数需要为pos , o , pre当检查到pos=-1时,return pre == 0否则,看当前是计算所有情况还是具体情况(doing)如果是所有情况且dp值!=-1,直接return否则就枚举0到end而支点o需要在最外层枚举出来*/#include<cstdio>#include<cstring>#include<algorithm>using namespace std;long long dp[19][19][2000];int digit[19];void init(){memset(dp,-1,sizeof(dp));}long long dfs(int pos , int o , int pre , bool doing){if(pos == -1)return pre == 0;if(!doing && dp[pos][o][pre] != -1)return dp[pos][o][pre];long long ans = 0;int end = doing ? digit[pos] : 9;for(int i = 0 ; i <= end ; i ++){int npre = pre;npre += (pos-o)*i;ans += dfs(pos-1 , o , npre , doing && i == end);}if(!doing)dp[pos][o][pre] = ans;return ans;}long long cal(long long x){int pos = 0;while(x){digit[pos++] = x % 10;x /= 10;}long long ans = 0;for(int o = 0 ; o < pos ; o ++){ans += dfs(pos-1 , o , 0 , 1);}return ans - (pos-1);//duplicate 0}int main(){init();int T;for(scanf("%d",&T) ; T--; ){long long left , right;scanf("%lld%lld",&left , &right);printf("%lld\n",cal(right) - cal(left - 1));}return 0;}/* ***********************************************Author :kuangbinCreated Time :2013/9/14 星期六 12:45:42File Name :2013成都网络赛\1007.cpp************************************************ */#pragma comment(linker, "/STACK:1024000000,1024000000")#include <stdio.h>#include <string.h>#include <iostream>#include <algorithm>#include <vector>#include <queue>#include <set>#include <map>#include <string>#include <math.h>#include <stdlib.h>#include <time.h>using namespace std;int dp[20][200000];int bit[20];int dfs(int pos,int num,bool flag){if(pos == -1)return num >= 0;if(num < 0)return 0;if(!flag && dp[pos][num] != -1)return dp[pos][num];int ans = 0;int end = flag?bit[pos]:9;for(int i = 0;i <= end;i++){ans += dfs(pos-1,num - i*(1<<pos),flag && i==end);}if(!flag)dp[pos][num] = ans;return ans;}int F(int x){int ret = 0;int len = 0;while(x){ret += (x%10)*(1<<len);len++;x /= 10;}return ret;}int A,B;int calc(){int len = 0;while(B){bit[len++] = B%10;B/=10;//cout<<bit[len-1]<<endl;}//cout<<F(A)<<endl;return dfs(len-1,F(A),1);}int main(){//freopen("in.txt","r",stdin);//freopen("out.txt","w",stdout);int T;int iCase = 0;scanf("%d",&T);memset(dp,-1,sizeof(dp));while(T--){iCase++;scanf("%d%d",&A,&B);printf("Case #%d: %d\n",iCase,calc());}return 0;}/** HDU 2089* 求一个区间内,不出现4和连续的62的数的个数。* 这题可以暴力打表。* 数位DP也可以做。**/#include <iostream>#include <stdio.h>#include <algorithm>#include <string.h>using namespace std;int dp[10][6];/** dp[i][0],表示长度为i,不存在不吉利数字* dp[i][7],表示长度为i,不存在不吉利数字,且最高位为2* dp[i][8],表示长度为i,存在不吉利数字*/void init(){dp[0][0]=1;dp[0][9]=0;dp[0][10]=0;for(int i=1;i<=6;i++){dp[i][0]=dp[i-1][0]*9-dp[i-1][11];//在最高位加上除4以外的9个数字,但要减掉2之前加上6dp[i][12]=dp[i-1][0];//在不含不吉利数字的最高位加上2dp[i][13]=dp[i-1][14]*10+dp[i-1][0]+dp[i-1][15];//在已有不吉利数字前加任意数字,或者无不吉利数字的最高位加4,或者在2前面加6}}int bit[10];int solve(int n){int len=0;int tmp=n;while(n){bit[++len]=n%10;n/=10;}bit[len+1]=0;int ans=0;bool flag=false;for(int i=len;i>=1;i--){ans+=dp[i-1][16]*bit[i];if(flag)//高位已经出现4或者62,后面随意ans+=dp[i-1][0]*bit[i];if(!flag&&bit[i]>4)ans+=dp[i-1][0];if(!flag&&bit[i+1]==6&&bit[i]>2)ans+=dp[i][17];if(!flag&&bit[i]>6)ans+=dp[i-1][18];if(bit[i]==4||(bit[i+1]==6&&bit[i]==2))flag=true;}if(flag)ans++;//这个数本身return tmp-ans;}int main(){//freopen("in.txt","r",stdin);//freopen("out.txt","w",stdout);init();int n,m;while(scanf("%d%d",&n,&m)==2){if(n==0 && m==0)break;printf("%d\n",solve(m)-solve(n-1));}return 0;}F(x):#include<cstdio>#include<cstring>#define maxn 16int dp[maxn][111111];int d[maxn];int n;long long tt;long long dfs(int len ,int pre ,bool fp){if(pre<0)return 0;//说明上一层枚举的数超过了上限,没有可用的情况if(!len)return 1;//说明上一层是个位.那么只需要把各个数累加起来就可以if(!fp&&dp[len][pre]!=-1)return dp[len][pre];//记忆化搜索int fpmax=fp?d[len]:9;//取该位取值的最大值int ret=0;for(int i=0;i<=fpmax;i++){//从最大长度向下,每一个长度的所有取值都要遍历到,//一旦该位的取值不是紧贴最大值,fp就false.ret+= dfs(len-1,pre-i*(1<<(len-1)),fp&&i==fpmax);}if(!fp)dp[len][pre]=ret;//记录结果return ret;}long long calc(long long a){int len=0;memset(d,0,sizeof(d));while(a){d[++len]=a%10;a/=10;}return dfs(len,tt,true);}int get(int x){int tmp=1;int ans=0;while(x){ans+=(x%10)*tmp;x/=10;tmp<<=1;}return ans;}int main(){long long a,b;int nc;scanf("%d",&nc);int d=1;memset(dp,-1,sizeof(dp));while(nc--){scanf("%I64d%I64d",&a,&b);tt=get(a);printf("Case #%d: %I64d\n",d++,calc(b));}return 0;}
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/dojintian/article/details/46955533