序列型动态规划--猜谜(三校联考试题)
来源:互联网 发布:热血无赖mac存档位置 编辑:程序博客网 时间:2024/06/15 02:09
【问题描述】
给出一个长度为N的数字字符串和一个数字T,要求插入最少的加号或者乘号,使得数字字符串的运算结果为T。运算符*号优先级高于+号,运算数可以有任意个前导0.
【输入格式】
输入不超过5组数据,每组数据两行。
每组数据的第一行为长度N,只包含0~9的数字字符串;第二行为一个数字T。
输入T<0表示输入结束。
【输出格式】
输出一个数字单独占一行,表示最少需要添加的运算符(+号或*号)数,无解输出-1.
【输入样例】
032089
5
333
9
00
-1
【输出样例】
3
2
【数据范围】
对于30%的数据,有1<=N<=10,0<=T<=50.
对于50%的数据,有1<=N<=15,0<=T<=200.
对于全部的数据,有1<=N<=20,0<=T<=300.
看起来就很恶心的题目:的优先级>+,所以可以把用连接的连续数字看成一组,分组动归。
状态方程:f[i][j]表示前i个数字和为j,添加的运算符的最小个数
f[i][j]= min{ f[k][x] +1 +g[k+1][i][j-x] | 1<=k< i && j-x>=0}
边界:f[i][j]=g[1][i][j]
g[i][j][x]表示在第i~j个数字之间插入*,得到x的最少插入值<组权值>
g[i][j][x]=min{ g[i][p][k/num[p+1][j]] +1 | k%num[p+1][j]==0}
边界:g[i][j][num[i][j]]=0 |num[i][j]<=T
num[i][j]表示第i~j个数字组成一个新的数字是多少(可以有前导0)
num[i][j]=num[i][j-1]+s[j]-‘0’
实现发现问题了:
1.num数组中,若i< j,则num[i][j]的值不应为0,而是-1,如果是0的话算g的时候有问题(不过好像循环计算g[i][j][k]时保证j>=i的话也没有问题,代码中没有用num[i][j]=max(0,num[i][j-1])*10+s[j]-‘0’ );
num[i][j]可能>long long ,那也没办法,就是负数,不过应该也不存在满足的,因为T<=300,不过在判断g[i][j][num[i][j]]=0的条件应该是 if(num[i][j]<=T && num[i][j]>=0);
2.循环到多大的值一点要注意;
3.f[i][j]的边界不是简单的inf,而是g[1][i][j],如果赋成inf则答案为-1;
4.关于g[i][j][0]的问题在代码中有写,卡了挺久的;
代码如下:
#include<cstdio>#include<iostream>#include<cmath>#include<cstring>#include<algorithm>#include<vector>using namespace std;typedef long long LL;const int maxn=25;const int inf=100000000;const int maxm=305;int T,f[maxn][maxm],n,g[maxn][maxn][maxm];char s[maxn];int num[maxn][maxn];void db(){ for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { for(int k=1;k<=T;k++) cout<<g[i][j][k]<<' '; cout<<endl; } cout<<endl; }}/* f[i][j]表示前i个数字和为j的最小插入数 f[i][j]=min{ f[k][x]+1+g[k+1][i][j-x] | 1<=k<i && j-x>=0} 边界:f[i][j]=g[1][i][j] g[i][j][k]表示用*把第i~j个数字连在一起乘积为k的最小代价 g[i][j][k]=min{ g[i][p][k/num[p+1][j]] +1 | k%num[p+1][j]==0} 注意每次g[i][j][0]可以提出来特判因为不能%0 边界:g[i][j][num[i][j]]=0 |num[i][j]<=T */void dp(){ for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) { for(int k=0;k<=T;k++) g[i][j][k]=inf; if(num[i][j]<=T && num[i][j]>=0) g[i][j][num[i][j]]=0;//位数实在太多了爆LL没有办法,不完美 } for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) { if(num[i][j]==0) g[i][j][0]=0; else { if(num[i][i]==0 || num[j][j]==0) g[i][j][0]=min(g[i][j][0],1); else { for(int p=i+1;p<j;p++) g[i][j][0]=min(g[i][j][0],g[i][p][0]+1); } } for(int k=1;k<=T;k++) { for(int p=i;p<j;p++) if(num[p+1][j]>0 && k%num[p+1][j]==0) { g[i][j][k]=min(g[i][p][k/num[p+1][j]]+1,g[i][j][k]); } } } for(int i=1;i<=n;i++) for(int j=0;j<=T;j++) f[i][j]=inf; for(int i=1;i<=n;i++) for(int j=0;j<=T;j++) { int t=f[i][j]; for(int k=1;k<i;k++) for(int x=0;x<=j;x++) t=min(t,f[k][x]+1+g[k+1][i][j-x]); f[i][j]=t; }// db();}int main(){ freopen("in.txt","r",stdin);// freopen("out.txt","w",stdout); while(scanf("%s",s+1)!=EOF) { n=strlen(s+1); memset(num,-1,sizeof(num)); scanf("%d",&T); if(T<0) break; for(int i=1;i<=n;i++) { num[i][i-1]=0; for(int j=i;j<=n;j++) { num[i][j]=num[i][j-1]*10+s[j]-'0'; if(num[i][j]>T) break; } } dp(); if(f[n][T]<inf) printf("%d\n",f[n][T]); else printf("-1\n"); } return 0;}
- 序列型动态规划--猜谜(三校联考试题)
- 【三校联考试题】猜谜 dp
- [bzoj2239] 猜谜 迭代深搜 or 动态规划
- 序列型动态规划
- 序列型动态规划
- 重庆市四校联考(南开出题) 解密游戏(Trie树 | | 动态规划)
- 动态规划-单调递增最长子序列(三)
- 动态规划(三)最长递增子序列
- 【三校联考试题】软件开发 二分+dp
- 面试题[动态规划]: 最长公共子序列
- Codevs5230【三校联考试题】 猴子(重庆一中高2018级信息学竞赛测验8) 解题报告
- 浅谈动态规划(三)
- 动态规划入门(三)
- 算法(三):动态规划
- 三、动态规划算法解最长公共子序列LCS问题(2011.12.13重写)
- 三、动态规划算法解最长公共子序列LCS问题(2011.12.13重写)
- 算法设计分析之三(动态规划,最长公共子序列)
- 动态规划三部曲之解决最长公共子序列问题(三)
- 两个节点添加osd
- 训练指南计数问题
- 进程和线程
- linux 下 c 编程2
- Common Subsequence HDU
- 序列型动态规划--猜谜(三校联考试题)
- navicat for mysql 创建存储过程时报错1064
- ListView嵌套GrideView item无法点击问题
- 用户权限
- mac进入某个目录快捷键
- redisTimple 对redis进行增加、删除、查找
- 安卓图片剪切,修正3星等图片旋转问题的工具类
- CoreText
- CODE[VS]1009 产生数