题意:给定一个字符串 输出回文子序列的个数 一个字符也算一个回文
很明显的区间dp 就是要往区间小的压缩!
#include<bits/stdc++.h> using namespace std; //input #define rep(i,x,y) for(int i=(x);i<=(y);++i) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m); #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s) #define LL long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) ////////////////////////////////// #define N 1005 #define inf 0x3f3f3f3f #define mod 10007 int dp[N][N]; int main() { int cas; cin>>cas; char s[1005]; int kase=0; while(cas--) { CLR(dp,0); RS(s+1); int n=strlen(s+1); rep(i,1,n) dp[i][i]=1;//长度为一的区间肯定是一个回文 rep(i,2,n) for(int j=i-1;j>=1;j--)//这样保证了 len从小到大! { dp[j][i]=(dp[j+1][i]+dp[j][i-1]-dp[j+1][i-1]+mod)%mod;//容斥原理 反正就是要将dp往区间短的转移 if(s[i]==s[j]) dp[j][i] = (dp[j][i]+dp[j+1][i-1]+1+mod)%mod;//如果两边相等 那么这两个数可与i+1 到j-1里所有回文序列组成一个回文序列 且它们自己也是一个回文序列所以要加一 } printf("Case %d: %d\n",++kase,dp[1][n]); } }
Poj2955 括号匹配(一)
给出一个的只有'(',')','[',']'四种括号组成的字符串,求 最多 有多少个括号满足题目里所描述的完全匹配。
状态转移方程:dp[i][j]表示第i~j个字符间的最大匹配字符数。
if(s[i] 与 s[j]匹配) dp[i][j] = d[[i+1][j-1] +2;
dp[i][j] = max(dp[i][j],dp[i][k]+dp[k+1][j]);
#include<bits/stdc++.h> using namespace std; //input #define rep(i,x,y) for(int i=(x);i<=(y);++i) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m); #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s) #define LL long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) ////////////////////////////////// #define N 1005 #define inf 0x3f3f3f3f #define mod 10007 int dp[N][N]; int main() { char s[N]; while(RS(s+1)==1) { CLR(dp,0); int n=strlen(s+1); rep(i,1,n-1) // if(s[i]=='('&&s[i+1]==')'||s[i]=='['&&s[i+1]==']') // dp[i][i+1]=1; rep(len,1,n) for(int i=1,j=i+len-1;j<=n;i++,j=i+len-1) { if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']')) dp[i][j] = dp[i+1][j-1]+2; //如果匹配,先更新 for(int k = i;k<j;k++)//区间合并 {//k<j dp[i][j] = max(dp[i][j],dp[i][k]+dp[k+1][j]); } } cout<<dp[1][n]<<endl; } }
整数划分
给出一个数n,要求在n的数位间插入(m-1)个乘号,将n分成了m段,求这m段的最大乘积。
样例输入
2
111 2
1111 2
样例输出
11
121
状态转移方程为
dp[i][j]=max(dp[i][j],dp[k][j-1]*num[k+1][i])
其中num[i][j]表示从s[i]到s[j]这段连续区间代表的数值。
#include<bits/stdc++.h> using namespace std; //input #define rep(i,x,y) for(int i=(x);i<=(y);++i) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m); #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s) #define LL long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) ////////////////////////////////// #define N 1005 #define inf 0x3f3f3f3f #define mod 10007 int dp[N][N]; int num[ N][N]; int main() { char s[1000]; int n,m; RS(s+1); RI(m); n=strlen(s+1); rep(i,1,n) { num[i][i]=s[i]-'0'; rep(j,i+1,n) num[i][j]=num[i][j-1]*10+s[j]-'0'; } rep(i,1,n) dp[i][0]=num[1][i]; rep(j,1,m-1)//乘号一个个放入 因为每次dp[i][j] 由dp[i][j-1]转移而来 所以要j从小到大开始枚举 就像之前的要len从小到大开始枚举 因为每一个长区间取决于短区间 rep(i,j+1,n) rep(k,j,i-1) dp[i][j]=max(dp[i][j],dp[k][j-1]*num[k+1][i] ); cout<<dp[n][m-1]; }