区间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 }
POJ 1651

 

划分回文串:

一个字符串,划分成最少的回文串。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 }
Uva 11584

 

posted @ 2017-02-27 22:31  rsqppp  阅读(137)  评论(0)    收藏  举报