DP

一、区间DP

1. 石子合并

(1) 相邻成圈取🏐:这道让我一直纠结于dp的最优子结构和贪心的局部最优,实际上到现在还不太能讲清楚,不过,像田忌赛马一样? 

   思路是:把圈石头变成直石头,以两堆合并到n堆合并为阶段,从l堆到r堆合并为状态,判断用k在l r之间分割并判断为决策。

 1 #include<bits/stdc++.h>
 2 #define mem(a) memset(a,0,sizeof(a))
 3 #define mem1(a) memset(a,-1,sizeof(a))
 4 #define fio ios::sync_with_stdio(false);cin.tie(0)
 5 #define ll long long
 6 #define mp make_pair
 7 #define inf 0x3f3f3f3f
 8 const int N=305;
 9 const int M=1e3+10;
10 const ll mod=998244353;
11 using namespace std;
12 int m,n,a[N],dpmax[N][N],dpmin[N][N],sum[N];
13 int main()
14 {
15     fio;
16     cin>>n;
17     memset(dpmin,0x3f,sizeof(dpmin));
18     mem1(dpmax);
19     for(int i=1;i<=n;i++)
20     {
21         cin>>a[i];
22         a[n+i]=a[i];
23         sum[i]=sum[i-1]+a[i];
24         dpmax[i][i]=dpmin[i][i]=0;
25     }
26     for(int i=n+1;i<=2*n-1;i++)
27     {
28         sum[i]=sum[i-1]+a[i];
29         dpmax[i][i]=dpmin[i][i]=0;
30     }
31 
32     for(int i=2;i<=n;i++)
33     {
34         for(int l=1;l+i-1<=2*n-1;l++)
35         {
36             int r=l+i-1;
37             for(int k=l;k<r;k++)
38             {
39                dpmax[l][r]=max(dpmax[l][r],dpmax[l][k]+dpmax[k+1][r]+sum[r]-sum[l-1]);
40                dpmin[l][r]=min(dpmin[l][r],dpmin[l][k]+dpmin[k+1][r]+sum[r]-sum[l-1]);
41             }
42         }
43     }
44     int mn=inf,mx=-inf;
45     for(int i=1;i<=n;i++)
46     {
47         mn=min(mn,dpmin[i][i+n-1]);
48         mx=max(mx,dpmax[i][i+n-1]);
49     }
50     cout<<mn<<endl<<mx<<endl;
51     return 0;
52 }
-O-

2. 租用游艇

 也是把从l到r的费用问题,转为比较从l到k+从k到r。

洛谷P1359 只要求求从1到n的最小花费,下面的代码包括从l到r和记录最优中介点(用s[][]记录,最后打印就递归就行)

P1359 租用游艇

3. 矩阵连乘:即a的列等于b的行,多个矩阵相乘时,其行列分别对应首尾矩阵,矩阵相乘次数是m*n*k。题目一般给出n个数,则代表n-1个矩阵,a[0],a[1]是第一个矩阵的行和列, a[1]也是第二个矩阵的行。也是在l和r中间找一个k,这样就有三个矩阵连乘的值。因为无论怎么分,最后这两个被k分隔的矩阵还是要乘起来,即a[l-1] * a[r] * a[k] 注意l-1,即l矩阵的行 

poj1651就是一道矩阵连乘题

poj 1651

 

二、背包问题

(1) 01背包

 1 int rec(int i,int j)
 2 {
 3     if(dp[i][j]>=0) return dp[i][j];
 4     int ans;
 5     if(i==n) //没有剩余物品
 6       ans=0;
 7     else if(j<w[i]) //钱不够
 8       ans=rec(i+1,j);
 9     else 
10       ans=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);
11     return dp[i][j]=ans;
12 }
13 void solve()
14 {
15     memset(dp,-1,sizeof(dp));
16     printf("%d\n",rec(0,W));
17 }
18  
记忆化搜索
 1 void solve1() //逆向
 2 {
 3    //dp[i][j]表示从第i个物品之后的物品中挑选花费总和小于j的物品 
 4    for(int i=n-1;i>=0;i--)
 5    {
 6        for(int j=0;j<=W;j++)
 7        {
 8            if(j<w[i])
 9              dp[i][j]=dp[i+1][j];
10            else 
11              dp[i][j]=max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]);
12        }
13    }
14    printf("%d\n",dp[0][W]);
15 }
16 
17 void solve2() //正向
18 {
19     //dp[i][j]表示从前i个物品中选出总花费不超过j的物品时总价值的最大值
20     for(int i=0;i<n;i++)
21     {
22         for(int j=0;j<=W;j++)
23         {
24             if(k<w[i])
25               dp[i+1][j]=dp[i][j];
26             else 
27               dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]+v[i]);
28         }    
29     }
30     printf("%d\n",dp[n][W]);
31 }
32 
33 void solve3() //两种状态
34 {
35     //dp[i][j]表示从前i个物品中选出总花费不超过j的物品时总价值的最大值
36     /* 
37        状态转移为从"前i个物品中选取总重量不超过j时的状态"向
38        "前i+1个物品中选取总重量不超过j"和
39        "从前i+1个物品中选取总重量不超过j+w[i]时的状态"的转移:
40     */
41     for(int i=0;i<n;i++)
42     {
43         for(int j=0;j<=W;j++)
44         {
45               dp[i+1][j]=max(dp[i+1][j],dp[i][j]);
46             if(j+w[i]<=W)
47               dp[i+1][j+w[i]]=max(dp[i+1][j+w[i]],dp[i][j+w[i]-w[i]]+v[i]);
48         }    
49     }
50     printf("%d\n",dp[n][W]);
51 }
DP

//两种方法都是O(nW)

二、LCS

1.可以参考《趣学算法》或这篇🥟

找到状态dp[i]][j]记录a中从0到i和b中从0到j最小公共子序列的长度。有三种可能,标出来就行。如果要求输出具体是哪几个字符,可以再开一个数组,记录是三种情况中的哪一种

如这题🍓

 1 #include<bits/stdc++.h>
 2 #include<iostream>
 3 #include<stack>
 4 #include<algorithm>
 5 #include<cstdio>
 6 #include<cmath>
 7 #include<cstring>
 8 #define fio ios::sync_with_stdio(false);cin.tie(0)
 9 #define mem(a) memset(a,0,sizeof(a))
10 #define memm(a) memset(a,inf,sizeof(a))
11 #define ll long long
12 #define ld long double
13 #define uL unsigned long long
14 #define pb push_back
15 #define inf 0x3f3f3f3f
16 using namespace std;
17 const int N=1e3+5;
18 const int M=1e9+5;
19 int n,m,t,x,a[N],q,y,b[N][N],vis[N],dp[N][N];
20 string s,d;
21 void print(int i,int j)
22 {
23     if(i==0||j==0) return;
24     if(b[i][j]==1)
25     {
26         print(i-1,j-1);
27         cout<<s[i-1];
28     }
29     else if(b[i][j]==2)
30         print(i-1,j);
31     else if(b[i][j]==3)
32         print(i,j-1);
33 
34  }
35 int main()
36 {
37     fio;
38     cin>>s>>d;
39     int len1=s.size(),len2=d.size();
40     for(int i=1;i<=len1;i++)
41     {
42         for(int j=1;j<=len2;j++)
43         {
44             //if(i==0||j==0) dp[i][j]=0,b[i][j]=0;
45 
46                 if(s[i-1]==d[j-1]) dp[i][j]=dp[i-1][j-1]+1,b[i][j]=1;
47                 else
48                 {
49                     if(dp[i-1][j]>dp[i][j-1])
50                         dp[i][j]=dp[i-1][j],b[i][j]=2;
51                     else
52                         dp[i][j]=dp[i][j-1],b[i][j]=3;
53                 }
54 
55         }
56     }
57     print(len1,len2);
58 
59 
60     return 0;
61 }
51Nod 1006

三、未归类

1. 编辑距离:即将一个字符串变换为另一个字符串所需要的最小编辑操作。跟LCS很像,也是三种可能,dp[i-1][j]+1就是 

 

posted @ 2019-07-18 14:50  XXrl  阅读(227)  评论(0)    收藏  举报