ACM预备队-week7(DP1)

1.[NOIP2005 普及组] 采药

题目链接:P1048 [NOIP2005 普及组] 采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

01背包,可以利用滚动数组优化为一维。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N=1010;
 4 int f[N];
 5 int main()
 6 {
 7     int T,M;
 8     cin>>T>>M;
 9     for(int i=1;i<=M;i++)
10     {
11         int v,w;
12         cin>>v>>w;
13         for(int j=T;j>=v;j--)
14         {
15             f[j]=max(f[j],f[j-v]+w);
16         }
17     }
18     cout<<f[T];
19     return 0;
20 }

2.最长上升子序列

题目链接:B3637 最长上升子序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

如果数据范围是1e4数量级以上,就不能用朴素算法了,O(N2)的复杂度

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N=5010;
 4 int a[N],f[N];
 5 int n;
 6 int main()
 7 {
 8     cin>>n;
 9     for(int i=1;i<=n;i++)cin>>a[i];
10     for(int i=1;i<=n;i++)
11     {
12         f[i]=1;
13         for(int j=1;j<i;j++)
14             if(a[j]<a[i])
15             {
16                 f[i]=max(f[i],f[j]+1);
17             }
18     }
19     int ans=0;
20     for(int i=1;i<=n;i++)ans=max(ans,f[i]);
21     cout<<ans;
22     return 0;
23 }

 3.最大子段和:

题目链接:P1115 最大子段和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

该算法更为简便之处是忽略了对子序列的寻找比较,而是根据规律直接找出最佳答案.

对于含有正数的序列而言,最大子序列肯定是正数,所以头尾肯定都是正数.我们可以从第一个正数开始算起,每往后加一个数便更新一次和的最大值;当当前和成为负数时,则表明此前序列无法为后面提供最大

子序列和,因此必须重新确定序列首项.

首先定义一个答案,必须在数据最小值范围之外,定义S是扫描到第i个数时,前 i-1 个数的最大子段和,将子段与0比较即可,反正是玄学。。。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N=2e5+10;
 4 int a[N];
 5 int main()
 6 {
 7     int res=-1e9,n,s=0;
 8     cin>>n;
 9     for(int i=0;i<n;i++)cin>>a[i];
10     for(int i=0;i<n;i++)
11     {
12         if(s<=0)s=0;
13         s+=a[i];
14         res=max(res,s);
15     }
16     cout<<res;
17     return 0;
18     
19 }

4.最长公共子序列(LCS)

题目链接:LCS - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

其中00表示不含i和j,01表示不含i但含j(这个j可能选也可能不选),10表示含i但不含j(这个i可能选也可能不选),11表示含i含j,但是,11有一个前提,就得是当a[i]==b[j]时

00:f[i-1,j-1]           01:f[i-1,j]         10:f[i,j-1]       11:if(a[i]==b[j])f[i-1][j-1]+1

但是因为00其实就是01和10的子集,所以可以不写00,此时01和10其实不是真正意义的选与不选,但是最大值只要包括了而且没有越界,就算情况重复也不影响max值

因为此题还要输出路径,所以还得倒着来一遍。用res表示路径。

状态方程解释很清晰链接:最长公共子序列 - 动态规划 Longest Common Subsequence - Dynamic Programming_哔哩哔哩_bilibili

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 
 5 
 6 using namespace std;
 7 
 8 const int N = 1010;
 9 
10 int n, m;
11 char a[N], b[N];
12 int f[N][N];
13 
14 int main()
15 {
16     cin >> n >> m;
17     cin >> a + 1 >> b + 1;
18 
19     for (int i = 1; i <= n; i ++)
20         for (int j = 1; j <= m; j ++)
21         {
22             if (a[i] == b[j]) f[i][j] =  f[i - 1][j - 1] + 1 ;
23             else f[i][j] = max(f[i - 1][j], f[i][j - 1]);
24             
25         }
26     cout << f[n][m] << endl;
27 
28     string res;
29     // 一个倒序的过程
30     for (int i = n, j = m; i && j; )
31     {
32         if (a[i] == b[j]) res += a[i], i --, j --;
33         else if (f[i - 1][j] > f[i][j - 1]) i --;
34         else j --;
35     }
36 
37     reverse(res.begin(), res.end());
38 
39     cout << res << endl;
40     return 0;
41 }

 

posted @ 2022-12-19 19:59  Zac-saodiseng  阅读(37)  评论(0)    收藏  举报