某大牛的数位DP

出处:http://blog.youkuaiyun.com/ACM_cxlove?viewmode=contents 


HDU 2089 不要62

http://acm.hdu.edu.cn/showproblem.php?pid=2089

不能出现4,或者相邻的62,这题可以暴力打表解决

具体的在代码里都有解释

[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstring>  
  3. #include<queue>  
  4. #include<cstdio>  
  5. #include<cmath>  
  6. #include<algorithm>  
  7. #define N 55  
  8. #define inf 1<<29  
  9. #define MOD 9973  
  10. #define LL long long  
  11. #define eps 1e-7  
  12. #define zero(a) fabs(a)<eps  
  13. #define equal(a,b) zero(a-b)  
  14. using namespace std;  
  15. int dp[10][3];  
  16. //dp[i][0],表示不存在不吉利数字  
  17. //dp[i][1],表示不存在不吉利数字,且最高位为2  
  18. //dp[i][2],表示存在不吉利数字  
  19. void Init(){  
  20.     memset(dp,0,sizeof(dp));  
  21.     dp[0][0]=1;  
  22.     for(int i=1;i<=6;i++){  
  23.         dp[i][0]=dp[i-1][0]*9-dp[i-1][1];  //在最高位加上除了4之外的9个数字,但是可能在2之前加了6  
  24.         dp[i][1]=dp[i-1][0];    //就是在原先不含不吉利数字的最高位加2  
  25.         dp[i][2]=dp[i-1][2]*10+dp[i-1][0]+dp[i-1][1];  //在已经有不吉利数字最高位加任意数字,或者在无吉利数字前加4,或者在2前面加4  
  26.     }  
  27. }  
  28. int slove(int n){  
  29.     int len=0,bit[10];  
  30.     int tmp=n;  
  31.     while(n){  
  32.         bit[++len]=n%10;  
  33.         n/=10;  
  34.     }  
  35.     bit[len+1]=0;  
  36.     int ans=0;  
  37.     bool flag=false;  
  38.     for(int i=len;i;i--){  
  39.         ans+=dp[i-1][2]*bit[i];    
  40.         if(flag)   //高位已经出现4或者62,后面的就随意  
  41.             ans+=dp[i-1][0]*bit[i];  
  42.         if(!flag&&bit[i]>4)  //高位可能出现4的情况  
  43.             ans+=dp[i-1][0];  
  44.         if(!flag&&bit[i+1]==6&&bit[i]>2)  //高位是6,后面一位可能出现2,这步debug了很久  
  45.             ans+=dp[i][1];  
  46.         if(!flag&&bit[i]>6)  //高位可能出现6,要把后面最高位为2计入  
  47.             ans+=dp[i-1][1];  
  48.         if(bit[i]==4||(bit[i+1]==6&&bit[i]==2))  //高位已经出现4或者62  
  49.             flag=true;  
  50.     }  
  51.     return tmp-ans;  
  52. }  
  53. int main(){  
  54.     int l,r;  
  55.     Init();  
  56.     while(scanf("%d%d",&l,&r)!=EOF&&l+r)  
  57.         printf("%d\n",slove(r+1)-slove(l));  
  58.     return 0;  
  59. }  


HDU 3555 BOMB

http://acm.hdu.edu.cn/showproblem.php?pid=3555

不能出现相邻的49,和上一题类似

[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstring>  
  3. #include<queue>  
  4. #include<cstdio>  
  5. #include<cmath>  
  6. #include<algorithm>  
  7. #define N 55  
  8. #define inf 1<<29  
  9. #define MOD 9973  
  10. #define LL long long  
  11. #define eps 1e-7  
  12. #define zero(a) fabs(a)<eps  
  13. #define equal(a,b) zero(a-b)  
  14. using namespace std;  
  15. LL dp[21][3],n;   
  16. int len,bit[21];  
  17. //dp[i][0]表示长度为i,包括49的个数  
  18. //dp[i][1]表示长度为i,没有49但是开头为9的个数  
  19. //dp[i][2]表示长度为i,没有49  
  20. void Init(){  
  21.     memset(dp,0,sizeof(dp));  
  22.     dp[0][2]=1;  
  23.     for(int i=1;i<20;i++){  
  24.         dp[i][0]=(LL)dp[i-1][0]*10+dp[i-1][1];  
  25.         dp[i][1]=dp[i-1][2];  
  26.         dp[i][2]=(LL)dp[i-1][2]*10-dp[i-1][1];  
  27.     }  
  28. }  
  29. int main(){  
  30.     Init();  
  31.     int t;  
  32.     scanf("%d",&t);  
  33.     while(t--){  
  34.         scanf("%I64d",&n);  
  35.         len=0;  
  36.         n++;  
  37.         while(n){  
  38.             bit[++len]=n%10;  
  39.             n/=10;  
  40.         }  
  41.         bit[len+1]=0;  
  42.         LL ans=0;  
  43.         bool flag=false;  
  44.         for(int i=len;i;i--){  
  45.             ans+=(LL)dp[i-1][0]*bit[i];  
  46.             if(flag)  
  47.                 ans+=(LL)dp[i-1][2]*bit[i];  
  48.             if(!flag&&bit[i]>4)  
  49.                 ans+=dp[i-1][1];  
  50.             if(bit[i]==9&&bit[i+1]==4)  
  51.                 flag=true;  
  52.         }  
  53.         printf("%I64d\n",ans);  
  54.     }  
  55.     return 0;  
  56. }  


UESTC 1307 WINDY 数

http://acm.uestc.edu.cn/problem.php?pid=1307

要求相邻的数差大于等于2

[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstring>  
  3. #include<queue>  
  4. #include<cstdio>  
  5. #include<cmath>  
  6. #include<algorithm>  
  7. #define N 100005  
  8. #define inf 1<<29  
  9. #define MOD 9973  
  10. #define LL long long  
  11. #define eps 1e-7  
  12. #define zero(a) fabs(a)<eps  
  13. #define equal(a,b) zero(a-b)  
  14. using namespace std;  
  15. int dp[15][10];  
  16. //dp[i][j]表示考虑i位的数中,最高为j的windy数  
  17. void Init(){  
  18.     memset(dp,0,sizeof(dp));  
  19.     for(int i=0;i<=9;i++)  
  20.         dp[1][i]=1;  
  21.     for(int i=2;i<=10;i++){  
  22.         for(int j=0;j<10;j++){  
  23.             for(int k=0;k<10;k++)  
  24.                 if(abs(j-k)>=2)  
  25.                     dp[i][j]+=dp[i-1][k];  
  26.         }  
  27.     }  
  28. }  
  29. int slove(int n){  
  30.     int len=0,bit[15];  
  31.     while(n){  
  32.         bit[++len]=n%10;  
  33.         n/=10;  
  34.     }  
  35.     bit[len+1]=0;  
  36.     int ans=0;  
  37.     //先把长度为1至len-1计入  
  38.     for(int i=1;i<len;i++)  
  39.         for(int j=1;j<10;j++)  
  40.             ans+=dp[i][j];  
  41.     //确定最高位  
  42.     for(int j=1;j<bit[len];j++)  
  43.         ans+=dp[len][j];  
  44.     for(int i=len-1;i;i--){  
  45.         for(int j=0;j<bit[i];j++)  
  46.             if(abs(j-bit[i+1])>=2)  
  47.                 ans+=dp[i][j];  
  48.         //如果高位已经出现非法,直接退出  
  49.         if(abs(bit[i]-bit[i+1])<2)  
  50.             break;  
  51.     }  
  52.     return ans;  
  53. }  
  54. int main(){  
  55.     Init();  
  56.     int l,r;  
  57.     while(scanf("%d%d",&l,&r)!=EOF)  
  58.         printf("%d\n",slove(r+1)-slove(l));  
  59.     return 0;  
  60. }  
### 问题解析 在牛客网的编程题中,“游游的整数切割”问题要求将一个整数按照某种方式切割成若干部分,使得这些部分满足特定的条件,通常是数学性质,例如每一部分都是某个数的倍数、满足回文性质、或者满足某种动态规划结构。 该问题通常属于**动态规划**或**贪心算法**范畴,具体解法取决于题目设定。假设题目要求将一个大整数切割成若干子串,使得每个子串构成的数字满足某种条件(如能被某个数整除),那么可以采用如下策略: - 将整数转换为字符串进行处理。 - 使用动态规划记录当前位置之前可以切割出的合法子串个数。 - 对于每个可能的切割点,检查子串是否满足条件。 下面是一个假设题意的 C++ 示例代码,假设题目要求找出能被3整除的所有子串组合方式数目: ```cpp #include <iostream> #include <vector> #include <string> using namespace std; int countDivisibleSubstrings(string num) { int n = num.size(); vector<int> dp(n + 1, 0); dp[0] = 1; // 空字符串视为一种方式 for (int i = 1; i <= n; ++i) { long long current = 0; for (int j = i - 1; j >= 0; --j) { current = current + (num[j] - '0') * pow(10, i - j - 1); if (current % 3 == 0) { dp[i] += dp[j]; } } } return dp[n]; } int main() { string num; cin >> num; cout << countDivisibleSubstrings(num) << endl; return 0; } ``` ### 算法说明 - 上述代码使用动态规划思想,`dp[i]`表示前`i`位数字可以被切割成若干个能被3整除的子串的方式数目。 - 内层循环尝试从位置`j`到`i`构成一个子串,并判断是否能被3整除。 - 如果可以,则将`dp[j]`加到`dp[i]`中,表示从`j`到`i`切割后的方式数。 ### 优化策略 - 若整数长度较长,可使用前缀和技巧加速子串数值计算。 - 对于非常大的整数,建议使用字符串处理而非转换为整型,以避免溢出问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值