地址:http://acm.hdu.edu.cn/showproblem.php?pid=2089
代码:
#include
#include
//#include
#include
#include
#include
using namespace std;
//#define M 1000000007
int main()
{
int i,j,k,m,n,dp[10][20]={0};
dp[0][0]=1;
for(i=1;i<=7;i++) //打表
{
for(j=0;j<=9;j++)
for(k=0;k<=9;k++)
if(j!=4&&k!=4&&!(j==6&&k==2))
dp[i][j]+=dp[i-1][k];
}
while(scanf("%d%d",&m,&n)>0,m||n)
{
int len1,len11[10]={0},len2,len22[10]={0};
len1=len2=0;
while(m)
{
len11[len1++]=m%10;
m/=10;
}
while(n)
{
len22[len2++]=n%10;
n/=10;
}
int ans=0;
for(i=len1-1;i>=0;i--) //求出m之前有多少数字符合情况
{
for(j=0;j=0;i--) //求出n之前有多少数字符合情况
{
for(j=0;j
地址:http://acm.hdu.edu.cn/showproblem.php?pid=3555
思路:纯数位dp,直接模板,不解释。
代码:
#include
#include
//#include
#include
#include
#include
using namespace std;
//#define M 1000000007
int main() //我这里连变量都懒得改了,不过需要注意INT64
{
__int64 i,j,n,k,t,dp[22][11][2]={0};
dp[0][0][0]=1;
for(i=1;i<=20;i++)
{
for(j=0;j<=9;j++)
for(k=0;k<=9;k++)
{
if(k==9&&j==4)
dp[i][j][1]+=dp[i-1][k][0];
else dp[i][j][0]+=dp[i-1][k][0];
dp[i][j][1]+=dp[i-1][k][1];
}
}
scanf("%I64d",&t);
while(t--)
{
scanf("%I64d",&n);
__int64 len2=0,len22[30]={0};
while(n)
{
len22[len2++]=n%10;
n/=10;
}
__int64 ans=0,bg=0;
for(i=len2-1;i>=0;i--)
{
for(j=0;j
地址:http://www.acm.uestc.edu.cn/problem.php?pid=1307
/*这里的主要问题在于处理位数小于输入边界数字的数*/
#include
#include
#include
//#include
using namespace std;
#define LL long long int
LL dp[20][20]={0};
void getdp()
{
int i,j;
for(i=0;i<=9;dp[1][i]=1,i++);
for(i=2;i<=10;i++)
{
for(j=0;j<=9;j++)
for(int k=0;k<=9;k++)
if(k-j<=-2||k-j>=2) dp[i][j]+=dp[i-1][k];
}
}
LL getans(LL m)
{
LL len=1,len1[20]={0},ans=0;
while(m)
{
len1[len++]=m%10;
m/=10;
}
for(int i=1;i0;i--)
{
for(int j=0;j=2||len1[i+1]-j<=-2) ans+=dp[i][j];
if(len1[i+1]-len1[i]<2&&len1[i+1]-len1[i]>-2) break;
}
/*本来在这里的代码是这样的
for(int i=len-1;i>0;i--)
{
for(int j=0;j=2||len1[i+1]-j<=-2) ans+=dp[i][j];
if(i!=len-1) ans+=(dp[i][0]+dp[i][1])
if(len1[i+1]-len1[i]<2&&len1[i+1]-len1[i]>-2) break; //但是这里有个break,所以不能全部加上,所以这里看了下别人的代码,改用三个for循环
}*/
return ans;
}
int main()
{
LL l,r;
getdp();
while(scanf("%lld%lld",&l,&r)>0)
printf("%lld\n",getans(r+1)-getans(l));
return 0;
}
地址:http://www.lightoj.com/volume_showproblem.php?problem=1140
题意:问两个数之间有多少个数字“0”。
思路:本来想练习下搜索做法,但是越做越迷。最后还是用了打表的方法。注意的地方有两处,一个是打表时关于数位0的处理,另一个是求答案时关于数位0的处理。
代码:
#include
#include
#include
#include
using namespace std;
#define LL long long
LL dp[30][10]={0};
void getdp()
{
for(int i=1;i<27;i++)
{
for(int j=0;j<=9;j++)
for(int k=0;k<=9;k++)
dp[i][j]+=dp[i-1][k];
dp[i][0]+=(LL)pow(10.0,i-1); //打表时关于数位0的处理
}
}
LL getans(LL m)
{
int len=0,num[30]={0};
LL ans=0,n=m;
while(n)
{
num[++len]=n%10;
n/=10;
}
for(int i=len;i>0;i--)
{
for(int j=0;j
地址:http://www.lightoj.com/volume_showproblem.php?problem=1032
题意:二进制数中连续两个‘1’出现次数的和
思路:把输入的数转为二进制,打表也打二进制的表。其他的和一般的数位DP一样。
代码:
#include
#include
#include
#include
using namespace std;
#define LL long long
LL dp[40][2];
void getdp()
{
memset(dp,0,sizeof(dp));
for(int i=2;i<40;i++)
{
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
dp[i][j]+=dp[i-1][k];
dp[i][1]+=(LL)pow(2.0,i-2); //这里注意一下,两个连续的1出现不止加一次就够了
}
}
LL getans(LL m)
{
int len=0,num[40]={0};
LL ans=0,n=m;
while(n)
{
num[++len]=n%2;
n/=2;
}
for(int i=len;i>0;i--)
{
for(int j=0;j
地址:http://poj.org/problem?id=3252
题意:求“round number”,该种数的要求是将数转化为二进制后其含有的数字0多余1。
思路:跟hdu3709平衡数很像,不过平衡数是要求左右相等,而此题是要求0大于1。处理方法差不多,就是注意下在len=0时return的情况就行了。还有就是注意一下首位数为0的情况,以及当首位数不为0是往下求组合数的时候关于0的判断。
代码:
#include
#include
#include
#include
using namespace std;
int dp[40][100][2],num[40]; //这里多开一维是因为关于0有两种情况,一个是包含(前面的数字有1),一个是不包含(前面的数字没1)
int dfs(int len,int pos,bool p,bool z) //这里z是表示是否有前面的数字有1
{
if(!len) return pos<=50; //对于len为0时的返回值
if(!p&&~dp[len][pos][z]) return dp[len][pos][z];
int ans=0,u=(p?num[len]:1);
for(int i=0;i<=u;i++)
{
if(!z&&!i) ans+=dfs(len-1,pos,i==u&p,0); //这里我写两个判断是强迫症,可以和在一起
else ans+=dfs(len-1,pos+i*2-1,i==u&p,1);
}
if(!p) dp[len][pos][z]=ans;
return ans;
}
int getans(int m)
{
if(m<0) return 0;
int len=0;
for(;m;m/=2)
num[++len]=m%2;
return dfs(len,50,1,0);
}
int main()
{
memset(dp,-1,sizeof(dp));
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",getans(r)-getans(l-1));
return 0;
}
地址:http://www.lightoj.com/volume_showproblem.php?problem=1068
题意:能被K整除且各位数字之和也能被K整除的数。
思路:先用打表的方法,但超时了。果断换成深搜,A了。
代码:
#include
#include
#include
#include
using namespace std;
int dp[15][110][110],num[20],k; //把k设成全局变量
int dfs(int len,int m,int n,bool p)
{
if(!len) return m==0&n==0;
if(!p&&~dp[len][m][n]) return dp[len][m][n];
int ans=0,u=p?num[len]:9;
for(int i=0;i<=u;i++)
ans+=dfs(len-1,(m+i)%k,(i*(int)pow(10.0,len-1)+n)%k,i==u&p);
if(!p) dp[len][m][n]=ans;
return ans;
}
int getans(int m)
{
int len=0;
for(;m;m/=10)
num[++len]=m%10;
return dfs(len,0,0,1);
}
int main()
{
int l,r,t,cas=1;
scanf("%d",&t);
while(t--)
{
memset(dp,-1,sizeof(dp)); //每次注意重置数组
scanf("%d%d%d",&l,&r,&k);
if(k>82) printf("Case %d: 0\n",cas++);
else printf("Case %d: %d\n",cas++,getans(r)-getans(l-1));
}
return 0;
}
#include
#include
#include
#include
using namespace std;
int dp[40][25],k,b,num[40];
int dfs(int len,int mak,bool p)
{
if (!len) return (mak==k);
if (mak>k) return 0;
if (!p&&dp[len][mak]!=-1) return dp[len][mak];
int u=p?num[len]:1;
int ans=0;
for(int i=0;i<=u&&i<=1;i++)
ans+=dfs(len-1,i?mak+1:mak,p&i==u);
if(!p) dp[len][mak]=ans;
return ans;
}
int getans(int m)
{
int len=0;
for(;m;m/=b)
num[++len]=m%b;
return dfs(len,0,1);
}
int main()
{
memset(dp,-1,sizeof(dp));
int x,y;
scanf("%d%d%d%d",&x,&y,&k,&b);
printf("%d\n",getans(y)-getans(x-1));
}