版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/monochrome00/article/details/82355754
HDU - 3652 - B-number
题目链接<http://acm.hdu.edu.cn/showproblem.php?pid=3652>
题意:
输出1~n中包含“13”且能被13整除的个数。(1 <= n <= 1000000000)
题解:
数位dp的关键就是状态的选取。
这道题的“13”是否被包含很好处理。要保证被13整除只需再添加一维记录余数即可。
#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
typedef long long ll;
int dp[20][2][2][20],dig[20];
int dfs(int len,bool yi,bool san,int mod,bool e){
if(!len) return san&&!mod;
if(!e&&~dp[len][yi][san][mod]) return dp[len][yi][san][mod];
int u=e?dig[len]:9,ret=0;
for(int i=0;i<=u;i++){
ret=(ret+dfs(len-1,i==1,san||(i==3&&yi),((mod*10)+i)%13,e&&i==u));
}
if(!e) dp[len][yi][san][mod]=ret;
return ret;
}
int solve(int x){
int cnt=0;
while(x){
dig[++cnt]=x%10;
x/=10;
}
return dfs(cnt,0,0,0,1);
}
int main()
{
int t,n;
memset(dp,-1,sizeof(dp));
while(scanf("%d",&n)!=EOF){
printf("%d\n",(solve(n)-solve(0)));
}
}
SPOJ - BALNUM - Balanced Numbers
题目链接<https://cn.vjudge.net/problem/SPOJ-BALNUM>
题意:
给出一段范围[A, B],问你在这个范围中平衡数的个数。
平衡数定义为每一位的数字,如果是奇数就出现偶数次,如果是偶数就出现奇数次,当然也可以不出现。
题解:
开始打算开个11维的数组,想想就很夸张。
其实这题可以用三进制的数来存储状态。表示没有出现,奇数次,偶数次。
=59 049。所以第二维开个60000大小就够了。
还有要注意前导零。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[20][60000],dig[20],a[20];
ll dfs(ll len,ll stt,bool e,bool fst){
if(!len){
if(!fst) return 0;
for(ll i=0;i<=9;i++){
if(i%2==1) if(stt/a[i]%3==1) return 0;
if(i%2==0) if(stt/a[i]%3==2) return 0;
}
return 1;
}
if(!e&&fst&&~dp[len][stt]) return dp[len][stt];
ll u=e?dig[len]:9,ret=0;
for(ll i=0;i<=u;i++){
ll tmp=stt;
if(i==0&&!fst);
else{
if(tmp/a[i]%3<2) tmp+=a[i];
else tmp-=a[i];
}
ret+=dfs(len-1,tmp,e&i==u,fst||(i!=0));
}
if(!e&&fst) dp[len][stt]=ret;
return ret;
}
ll solve(ll x){
ll cnt=0;
while(x){
dig[++cnt]=x%10;
x/=10;
}
return dfs(cnt,0,1,0);
}
int main(){
int t;
ll n,m;
scanf("%d",&t);
a[0]=1;
for(ll i=1;i<=10;i++)
a[i]=a[i-1]*3;
memset(dp,-1,sizeof(dp));
while(t--){
scanf("%lld%lld",&n,&m);
printf("%lld\n",solve(m)-solve(n-1));
}
}
HDU - 4352 - XHXJ's LIS
题目链接<http://acm.hdu.edu.cn/showproblem.php?pid=4352>
题意:
求出[L,R]中最长上升子序列为k的个数。
题解:
通过nlogn求上升子序列的方法记录状态:
根据状压的思想,用一个10位的数字记录0~9是否在序列内,每次新加入一个数就把大于它的最小值替换掉。
把k也放在状态里面,不然会超时。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dg[20],dp[20][1<<12][12];
ll dfs(ll len,ll stt,ll maxn,bool e,bool fst,ll k){
if(!len) return maxn==k;
if(!e&&fst&&~dp[len][stt][k]) return dp[len][stt][k];
ll u=e?dg[len]:9,ret=0;
for(ll i=0;i<=u;i++){
if(i==0&&!fst){
ret+=dfs(len-1,0,0,e&&i==u,0,k);
continue;
}
if(stt&(1<<i)){
ret+=dfs(len-1,stt,maxn,e&&i==u,fst||i>0,k);
continue;
}
ll tmp=stt; bool flag=false;
for(ll j=i+1;j<=9;j++){
if(stt&(1<<j)){
tmp-=1<<j;
tmp+=1<<i;
flag=true;
break;
}
}
if(flag){
ret+=dfs(len-1,tmp,maxn,e&&i==u,fst||i>0,k);
continue;
}
else{
tmp+=1<<i;
ret+=dfs(len-1,tmp,maxn+1,e&&i==u,fst||i>0,k);
}
}
if(!e&&fst) dp[len][stt][k]=ret;
return ret;
}
ll solve(ll x,ll k){
ll cnt=0;
while(x){
dg[++cnt]=x%10;
x/=10;
}
return dfs(cnt,0,0,1,0,k);
}
int main(){
ll t,n,m,k,cs=0;
scanf("%lld",&t);
memset(dp,-1,sizeof(dp));
while(t--){
scanf("%lld%lld%lld",&n,&m,&k);
printf("Case #%lld: ",++cs);
printf("%lld\n",solve(m,k)-solve(n-1,k));
}
}