题意:给定一个长度为2n的串,每个字符或者是数字,或者是?。?的位置也应该是一个字符表示相应的数字,但是看不清了。问把?还原成数字,使得前n个数字的乘积等于后n个数字的乘积的还原方法有多少种,不等于的又有多少种?

输入:
2
2??3
输出:
4
96

样例解释:两个问号填数字能符合的四种是:2003、2323、2643、2963。

思路:做过的最难的一道dp,同学讲给我的思路。

1.首先处理乘积为0的情况。这个相对比较简单,只需用分“有已知的0”和“没有已知的0”两种情况讨论,
用组合数学的知识就可以很快计算出来,略过。

2.接下来考虑乘积不为0的情况。(注意此情况下一定不会有已知的0。)
考虑到最终乘积的质因数只有2,3,5,7四种,因此,
用F[i][a][b][c][d]表示:用i个"?"组成2^a*3^b*5^c*7^d的方案数。

在转移方程中从1到9枚举最后一个"?"的取值x,假设x=2^(a_x)*3^(b_x)*5^(c_x)*7^(d_x),有
F[i][a][b][c][d]=sum{F[i-1][a-a_x][b-a_x][c-c_x][d-d_x]}

初值F[0][0][0][0][0]=1,其他都是0。

假设:
前半部分有ml个问号,且所有已知数字的乘积是al,bl,cl,dl;
后半部分有mr个问号,且所有已知数字的乘积是ar,br,cr,dr;
则最终答案为sum{F[ml][a-al][b-bl][c-cl][d-dl]*F[mr][a-ar][b-br][c-cr][d-dr]}+(乘积为0的方案),其中a,b,c,d遍历了所有可能的取值。

时间复杂度:
乘积为0的部分可在O(n)内解决
动态规划部分由于a<=3n,b<=2n,c<=n,d<=n,所以可在O(n^5)内解决

实现难点:
1. 最终答案需要高精度,不过F用long long就可以了。
2. F的空间复杂度较高,可以使用滚动数组或者类似背包问题的那种办法减少空间开销。

写的时候WA和TLE了几次,对着官网的测试数据debug了45个小时才过,够酸爽~~

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 18
#define A N*3+2
#define B N*2+2
#define C N+2
#define D N+2
char str[(N<<1)+5];
int id,n,nl,nr;
long long ml,mr;
int res[(N<<1)+10],len;
long long dp[2][A][B][C][D],dpt[A][B][C][D];
int fac[13][4] = {//fac[1...9]表示前9个数字,fac[11]和fac[12]表示左边(右边)已经填好数字的积{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,1,0,0},{2,0,0,0},{0,0,1,0},{1,1,0,0},{0,0,0,1},{3,0,0,0},{0,2,0,0}};
int base[4] = {2,3,5,7};
void jinwei(){//处理进位int i,j;for(i = 0;i<=nl+nr;i++){j = res[i]/10;res[i] %= 10;res[i+1] += j;}len = nl+nr;while(len >0 && !res[len])len--;
}
void add(long long a,long long b){//将a*b累加到输出数组,因为a*b可能溢出long longint i,j;if(!a || !b)return;for(i = 0;a;i++){long long num = a%10;long long x = b*num;for(j = i;x;j++){res[j] += x%10;x /= 10;}a /= 10;}
}
long long pow(int x,int y){//实现幂函数,直接用math库的math会丢失精度long long sum = 1;while(y--)sum *= x;return sum;
}
int zero_case(){int i,j;long long t;if(!ml && !mr){             //如果两边都出现了0,最简单的情形res[len = nl+nr] = 1;return 1;}else if(ml && mr){          //如果两边都没出现add(pow(10, nl)-pow(9, nl),pow(10, nr)-pow(9, nr));//这是两边乘积为0的数量return 3;}else{                       //只有一边出现0if(!ml){t = pow(10, nr) - pow(9, nr);i = nl;}else{t = pow(10, nl) - pow(9, nl);i = nr;}for(j = i;t;j++){res[j] = t%10;t /= 10;}len = j-1;return 2;}return 0;
}
void change(){                  //算出左边(右边)已有的积的幂表示int i;for(i = 0;i<4;i++){while(ml && ml%base[i] == 0){fac[11][i]++;ml /= base[i];}while(mr && mr%base[i] == 0){fac[12][i]++;mr /= base[i];}}
}
int check(int a,int b,int c,int d,int i){return (a-fac[i][0]>=0)&&(b-fac[i][1]>=0)&&(c-fac[i][2]>=0)&&(d-fac[i][3]>=0);
}
int solve(int n){int i,j,p,q=0,a,b,c,d,need;memset(dp, 0, sizeof(dp));dp[0][0][0][0][0] = 1;memcpy(dpt, dp[0], sizeof(dp[0]));for(i = 1;i<=n;i++){q = i&1;            //滚动数组p = !q;for(d = 0;d<=i;d++)for(c = 0;c <= i-d;c++)for(b = 0;(need=(b+1)/2+d+c)<=i;b++)//注意判断条件,算作剪枝,不写则TLEfor(a = 0;(((b&1)?(a-1):a)+2)/3<=i-need;a++)for(j = 1;j<=9;j++)if(check(a,b,c,d,j))dp[q][a][b][c][d] += dp[p][a-fac[j][0]][b-fac[j][1]][c-fac[j][2]][d-fac[j][3]];if(i == min(nl,nr))     //记录一下memcpy(dpt, dp[q], sizeof(dp[q]));memset(dp[p], 0, sizeof(dp[p]));//注意memset的写法}return q;
}
void updateres(int id){int a,b,c,d,need;for(d = 0;d<=n;d++)for(c = 0;c <= n-d;c++)for(b = 0;(need=(b+1)/2+d+c)<=n;b++)for(a = 0;(((b&1)?(a-1):a)+2)/3<=n-need;a++)if(check(a, b, c, d, 11) && check(a, b, c, d, 12)){if(nl <= nr)add(dpt[a-fac[11][0]][b-fac[11][1]][c-fac[11][2]][d-fac[11][3]],dp[id][a-fac[12][0]][b-fac[12][1]][c-fac[12][2]][d-fac[12][3]]);elseadd(dp[id][a-fac[11][0]][b-fac[11][1]][c-fac[11][2]][d-fac[11][3]],dpt[a-fac[12][0]][b-fac[12][1]][c-fac[12][2]][d-fac[12][3]]);}jinwei();
}
int main(){int i,j;nl = nr = len = 0;ml = mr = 1;memset(res, 0, sizeof(res));scanf("%d",&n);scanf("%s",str);for(i = 0;i<n;i++)if(str[i] == '?')nl++;elseml *= str[i]-'0';for(i = n;i<n*2;i++)if(str[i] == '?')nr++;elsemr *= str[i]-'0';if(nl+nr == 0){             //没有问号的情况if(ml == mr)printf("1\n0");elseprintf("0\n1");return 0;}j = zero_case();if(j == 1){                 //两边都出现了数字0for(i = len;i>=0;i--)printf("%d",res[i]);printf("\n0");return 0;}else if(j == 3){            //两边都没出现数字0change();id = solve(max(nl,nr));updateres(id);}for(i = len;i>=0;i--)printf("%d",res[i]);putchar('\n');for(i = nl+nr-1;i>=0;i--)   //有所有的情况减去符合的情况就是不符合的情况res[i] = 9-res[i];res[nl+nr] = 0;add(1,1);jinwei();for(i = len;i>=0;i--)printf("%d",res[i]);return 0;
}

poj 1608 dp(Banal Ticket)相关推荐

  1. POJ 1608 Banal Tickets 笔记

    2N位数,部分数字模糊不清.如果前N位数的乘积等于后N位的乘积被认为是 interesting数,否则是banal数.求可能有多少 interesting数,多少 banal数. 

  2. POJ 3017 DP + 单调队列 + 堆

    题意:给你一个长度为n的数列,你需要把这个数列分成几段,每段的和不超过m,问各段的最大值之和的最小值是多少? 思路:dp方程如下:设dp[i]为把前i个数分成合法的若干段最大值的最小值是多少.dp转移 ...

  3. POJ 1159 (DP)

    题目:http://poj.org/problem?id=1159 思路: 找出原串的最长回文子串,当然这里说的回文子串可以不连续.用原串的长度减去最长回文子串的长度即可得出结果. 设原串a[5001 ...

  4. POJ 1037 DP

    题目链接: http://poj.org/problem?id=1037 分析: 很有分量的一道DP题!!! (参考于:http://blog.csdn.net/sj13051180/article/ ...

  5. POJ 1661 DP

    Help Jimmy Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 11071   Accepted: 3607 Descr ...

  6. POJ 3666 dp

    题意 传送门 POJ 3666 基本思路是对 N 个位置枚举所有可能高度,并 dp 求最大值.可能高度取 N 个位置的高度即可,排序以方便顺序 dp.对于升序的情况 dp[i][j]=max{dp[i ...

  7. POJ 3666(DP)

    题目链接:http://poj.org/problem?id=3666 题目大意:给一个串,要求修改最少使得串满足非递增或非递减 题目思路:刚开始没想到,后来经学长提醒如果串里的数字变1000可以怎么 ...

  8. POJ 3257 DP

    题意: 思路: 用vector存上本出发点能到的地方&成本&有趣指数(用结构体保存) 然后DP就好了 f[i][j]表示到了i 成本为j的有趣指数最大是多少 f[vec[i][k].e ...

  9. poj 2392 dp 不是很懂哎!!!Space Elevator

    大意:有K种block去建塔,每种每个都有一个高度H,用了当前的block塔的高度不能超出a,和每种的数量.求塔最高能建多高. 分析:这题就是一个多重背包,但有一点变动,必须先以a从小到大排序,因为如 ...

最新文章

  1. 这个能快速发表Cell,Nature,Molecular cell的分析技术你要错过吗?
  2. POJ 3461 KMP
  3. 树莓派wifi环境下初始化及环境配置
  4. 操作系统原理二进程切换,调度
  5. windows xp安装php7,在Windows XP下安装Apache+MySQL+PHP环境
  6. I春秋——web Write up(三)
  7. python 查看 .npy文件 和 .pkl 文件的方法
  8. matlab如何提高运算速速,如何提高以下程序的运算速度及有选择性的保存数据?...
  9. oracle 9i 只读模式,我的oracle 9i学习日志(6)--Starting Up and shutting down a Database
  10. maven创建父项目和子项目
  11. mysql 启动服务1067_windows无法启动MySQL服务报错1067的解决方法
  12. 微信小程序流量主+直播开通和编码指南
  13. bzoj 1878: [SDOI2009]HH的项链(主席树)
  14. mybatis mysql 模糊查询语句_mybatis+Spring mysql的模糊查询问题
  15. 【干货】GRU神经网络
  16. OSPF邻接关系建立过程
  17. learning ddr DLL-off mode
  18. 射电天文偏振线的绘制
  19. 《东周列国志》第二十八回 里克两弑孤主 穆公一平晋乱
  20. 网站搜索引擎优化诊断

热门文章

  1. 微信公众平台消息接口使用指南
  2. 工作记录 01-02-2018 至 03-16-2018
  3. python 时间记录
  4. C# openfiledialog文件单选和多选
  5. 斗地主叫牌、出牌、跟牌和打牌原则
  6. 苹果手机测距离_重量仅6g,智能距离检测,安卓苹果平板手机都能用,声光多级提醒...
  7. Api升级28适配填坑(一)
  8. vSphere6.7中WindowsServer2012r2虚拟机磁盘扩容
  9. 教你如何用Python抓取QQ音乐歌单及分析
  10. 手写体数字识别的两种方法