区间DP
二叉加分树:注意,从i到n的最大值应该由后面到部分区间构造出来,即i应该倒序枚举,一开始想到是用记忆化搜索,枚举断点k值,也是犯了枚举起点到错误,导致根本构造不出答案,已经是不会做啦,直接就看题解啦,代码其实和参考的完全一样:算是抄啦吧------不过感觉dp还是不能这样,这个真的要思维,以后必须想,避免依赖题解,实际上我从这个题学到到东西很少啊
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<string> 5 #include<queue> 6 #include<algorithm> 7 8 9 using namespace std; 10 11 const int maxn = 100; 12 int tree[maxn]; 13 long long int f[50][50]; 14 long long int g[50][50]; 15 void print(int l,int r) 16 { 17 if(l > r) return; 18 int k = g[l][r]; 19 cout<<k<<" "; 20 print(l,k-1); 21 print(k+1,r); 22 } 23 24 int main() 25 { 26 int n; 27 scanf("%d",&n); 28 for(int i = 1;i <= n;i++) 29 { 30 scanf("%d",&tree[i]); 31 f[i][i] = tree[i]; 32 g[i][i] = i; 33 } 34 for(int i = n;i > 0;i--) 35 { 36 for(int j = i+1;j <= n;j++) 37 { 38 for(int k = i;k <= j;k++) 39 { 40 int x = f[i][k-1]; 41 int y = f[k+1][j]; 42 if(x == 0) x = 1; 43 if(y == 0) y = 1; 44 if(f[i][j]<(x*y+tree[k])){ 45 f[i][j] = x*y+tree[k]; 46 g[i][j] = k; 47 } 48 } 49 } 50 } 51 cout<<f[1][n]<<endl; 52 print(1,n); 53 cout<<endl; 54 return 0; 55 }
昨天回教室路上想啦下,觉得记忆化还是可以滴,体育课码之。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 #include<queue> 6 7 8 using namespace std; 9 int n; 10 const int maxn = 200; 11 long long int f[maxn][maxn]; 12 int g[maxn][maxn]; 13 int tree[maxn]; 14 int dp(int l,int r) 15 { 16 if(l > r) return 0; 17 long long int& ans = f[l][r]; 18 if(ans > 0) return ans; 19 ans = 1; 20 for(int i = l;i <= r;i++) 21 { 22 int x = dp(l,i-1); 23 int y = dp(i+1,r); 24 if(x==0) x = 1; 25 if(y==0) y = 1; 26 if(x*y+tree[i] > ans) 27 { 28 ans = x*y+tree[i]; 29 g[l][r] = i; 30 } 31 } 32 return ans; 33 } 34 void print(int l,int r) 35 { 36 if(l > r) return; 37 int k = g[l][r]; 38 printf("%d ",k); 39 print(l,k-1); 40 print(k+1,r); 41 42 } 43 44 int main() 45 { 46 scanf("%d",&n); 47 for(int i = 1;i <= n;i++) 48 { 49 scanf("%d",&tree[i]); 50 f[i][i] = tree[i]; 51 g[i][i] = i; 52 } 53 int ans = dp(1,n); 54 cout<<ans<<endl; 55 print(1,n); 56 cout<<endl; 57 return 0; 58 }
最优矩阵链乘;
f(l,r) = min(f(l,k)+f(k,r)+N[l]*N[k]*N[r],f[l][r]) l < k < r;
码一下就好;
思路,长区间的最优可以由短区间的最优构造,边界是两个矩阵相乘,对应在程序里就是三个数相乘,枚举区间出度,递推即可。
经验,DP不要看别人的状态转移方程,否则,你自己想的永远都不会写对的。虽然你自己想的是对的。
代码如下:
1 #include<iostream> 2 #include<cstring> 3 #include<vector> 4 #include<cstdio> 5 using namespace std; 6 const int maxn = 1010; 7 int N[maxn]; 8 int f[maxn][maxn]; 9 int main() 10 { 11 int n; 12 scanf("%d",&n); 13 for(int i = 1;i<= n;i++) scanf("%d",&N[i]); 14 15 for(int i = 2;i <= n;i++) 16 for(int j = 1;j <= n-i;j++) 17 { 18 int l = j,r = j+i; 19 f[l][r] = 1e8; 20 for(int k = l+1;k < r;k++) 21 f[l][r] = min(f[l][k]+f[k][r]+N[l]*N[k]*N[r],f[l][r]); 22 } 23 printf("%d",f[1][n]); 24 return 0; 25 }
划分回文串:
一个字符串,划分成最少的回文串。fastcar = 7;aaadbccb = 3;racecar = 1;
同样,短区间可以构造出长区间的最优解,f(i) = min{f(j)+1|s[j+1~i] 是回文串};
可以枚举长度,再枚举起点,再枚举判断是否回文,状态(n2)个,转移花费(n)总计O(n3);
优化,直接预处理串是否为回文串,复杂度实现O(1)转移。
代码如下
1 #include<iostream> 2 #include<cstring> 3 #include<vector> 4 #include<cstdio> 5 #include<algorithm> 6 using namespace std; 7 const int maxn = 1010; 8 char s1[maxn]; 9 int f[maxn]; 10 bool Is_hw[maxn][maxn]; 11 12 void init() 13 { 14 memset(Is_hw,false,sizeof(Is_hw)); 15 for(int M = 0;M < strlen(s1);M++){ 16 Is_hw[M][M] = true; 17 for(int i = 1;i <= M;i++){ 18 if(s1[M-i] == s1[M+i]) Is_hw[M-i][M+i] = true; 19 else break; 20 } 21 for(int i = 1;i <= M;i++){ 22 if(s1[M-i] == s1[M+i-1]) Is_hw[M-i][M+i-1] = true; 23 else break; 24 } 25 } 26 } 27 28 int main() 29 { 30 int T; 31 scanf("%d",&T); 32 while(T--){ 33 scanf("%s",s1); 34 init(); 35 for(int i = 0;i < strlen(s1);i++) f[i] = 1e9; 36 f[0] = 1; 37 for(int i = 1;i < strlen(s1);i++){ 38 f[i] = f[i-1]+1; 39 for(int j = 0;j <= i;j++){ 40 if(Is_hw[j][i]) 41 { 42 if(j == 0) f[i] = 1; 43 else f[i] = min(f[j-1]+1,f[i]); 44 } 45 } 46 } 47 printf("%d\n",f[strlen(s1)-1]); 48 49 } 50 return 0; 51 }

浙公网安备 33010602011771号