开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第19天,点击查看活动详情
股票买卖Ⅲ
解题思路: 这一题如果用动态规划解的话,只需要将股票买卖Ⅳ中的K该位3即可。
前后缀分解
代码:
#include<iostream>
using namespace std;
const int N = 1e5+10;
int dp1[N],dp2[N];
int a[N];
int main()
{
int n;
cin>>n;
for(int i = 1; i <= n; i ++) cin>>a[i];
int t = 1e5;
for(int i = 1; i <= n; i ++)
{
t = min(t,a[i]);//记录前面最小的数
dp1[i] = max(dp1[i-1],a[i] - t);
}
t = -1;
for(int i = n; i >= 1; i --)
{
t = max(t,a[i]);//因为是从后面开始记录,那么取后面最大的赚取的利润才最大。
dp2[i] = max(dp2[i+1],t - a[i]);
}
int res = -1;
for(int i = 1; i <= n; i ++)
res = max(res,dp1[i] + dp2[i+1]);
cout<<res;
return 0;
}
股票买卖Ⅳ
解题思路:
对于什么时候是j什么时候是j-1的问题,我们需要从状态表示的含义进行出发;我们以这个为例来分析,
对于我们从有货变成无货的话,那么意义应该是前i-1个物品,我们已经交易完j-1次交易,且目前有第j张股票,我们将其卖去,得的价值是今天股票的价值因为今天股票的价格是固定的,我们只需将前边的收益最大化。而这正好是的含义。
同时对于状态机,状态机的入口全部初始化为0,非入口全初始化为无穷。
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5 + 10;
int n , w[N] , K;
int f[N][110][2];
int main(){
cin>>n>>K;
for(int i = 1 ; i <= n ; ++i)cin>>w[i];
memset(f , -0x3f , sizeof f );
for(int i = 0 ; i <= n ; ++i)f[i][0][0] = 0;
for(int i = 1 ; i <= n ; ++i)
for(int j = 1 ; j <= K ; ++j){
f[i][j][0] = max(f[i-1][j][1] + w[i] , f[i-1][j][0]);
f[i][j][1] = max(f[i-1][j][1],f[i-1][j-1][0] - w[i]);
}
int res = 0;
for(int i = 0 ; i <= K ; ++i)res = max(res , f[n][i][0]);
cout<<res<<endl;
return 0 ;
}
股票买卖Ⅴ
状态机模型
我们不能遗落任何一条转移的边
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1E5 + 10;
int n ;
int w[N];
int f[N][3];
int main(){
cin>>n;
for(int i = 1 ; i <= n ; ++i)cin>>w[i];
memset(f , -0x3f , sizeof f);
for(int i = 0 ; i <= n ;++i)f[i][0] = 0;
for(int i = 1; i <= n ; ++i){
f[i][0] = max(f[i-1][0] , f[i-1][2]);
f[i][1] = max(f[i-1][0] - w[i] ,f[i-1][1]);
f[i][2] = f[i-1][1] + w[i];
}
cout<<max(f[n][0] ,max(f[n][1] ,f[n][2]))<<endl;
return 0;
}
KMP + 状态机
设计密码
解题思路: 刚开始我的想法是表示第i位字符的时候,该字符是子串的字符和不是子串的字符的数量。不过这样就有一个问题:表示第i位填的是子串中的字符,那么能转移到它的状态可以是如果是后者我们无法知道i-1位的是子串的哪一个字符,那么我们就无法知道能填的字符数量。因此我们需要增加维度,那么第二维的含义就很关键,如果仅仅表示的是子串中的字符,我们还是无法辨别是否与子串相同,那么我们就可以借助KMP中的f数组:的含义定义第二维,那么有(因为该题是与子串相关的,而KMP就是处理子串匹配的)
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 55 , mod = 1e9 + 7;
int n;
char s[N];
int f[N][N];
int main(){
cin>>n>>s+1;
int len = strlen(s+1);
int next[N] = {0};
for(int i = 2 , j = 0 ; i <= len ; ++i){
while(j && s[j+1] != s[i]) j = next[j];
if(s[j+1] == s[i])j++;
next[i] = j;
}
f[0][0] = 1;
for(int i = 0; i <n ; ++i)
for(int j = 0 ; j < len ; ++j)
for(int k = 'a' ; k <= 'z' ; ++k){
int u = j ;
while(u && k != s[u+1]) u = next[u];
if(s[u+1] == k)u++;
if(u < len) f[i+1][u] = (f[i+1][u] + f[i][j]) % mod;
}
int res = 0;
for(int i = 0 ; i < len ; ++i) res = (res + f[n][i]) % mod;
cout<<res<<endl;
return 0;
}
AC自动机 + 状态机
修复DNA
解题思路:
首先这个题和上一题的解题思路是类似的,其次为什么想到AC自动机呢,因为AC自动机就是解决多串匹配的问题。
至于动态规划的过程我们类似上一题
这题我们分为AC自动机的建立与动态规划计算两个部分来解决。
- AC自动机的建立:1、依据每一个子串建立Trie树(利用插入函数),2、从树根开始向下依次计算出每一个节点的后缀节点 (建立AC自动机的过程)
- 动态规划状态的计算
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 1010 , INF = 0x3f3f3f3f;
int n , m;
int tr[N][4] , dar[N] ,idx; //Trie
int q[N] , pre[N]; //AC自动机
char str[N] ;
int f[N][N];
int get(char c){
if(c == 'A')return 0;
if(c == 'T')return 1;
if(c == 'G')return 2;
return 3;
}
void insert(){
int p = 0;
for(int i = 0 ; str[i] ; ++i){
int k = get(str[i]);
if(!tr[p][k])tr[p][k] = ++idx;
p = tr[p][k];
}
dar[p] = 1;
}
void build()
{
int hh = 0 , tt = -1;
for(int i = 0 ; i < 4 ; ++i)
if(tr[0][i]) q[++tt] = tr[0][i];
while(hh <= tt)
{
int t = q[hh++];
for(int i = 0 ; i < 4 ;++i){
int p = tr[t][i];
if(!p) tr[t][i] = tr[pre[t]][i];
else{
pre[p] = tr[pre[t]][i];
q[++tt] = p;
dar[p] |= dar[pre[p]];
}
}
}
}
int main(){
int T = 1;
while(cin>>n , n ){
memset(tr , 0 ,sizeof tr);
memset(dar , 0 , sizeof dar);
memset(pre , 0 ,sizeof pre);
idx = 0;
for(int i = 1 ; i <= n ; ++i){
cin>>str;
insert();
}
build();//建立AC自动机
cin>>str+1;
m = strlen(str+1);
memset(f , 0x3f ,sizeof f);
f[0][0] = 0;
for(int i = 0 ; i < m ; ++i)
for(int j = 0 ; j <= idx ; ++j)
for(int k = 0 ; k < 4 ; ++k){
int t = get(str[i+1]) != k;
int p = tr[j][k];
if(!dar[p])
f[i+1][p] = min(f[i+1][p] ,f[i][j] + t);
}
int res = INF;
for(int i = 0 ; i <= idx ; ++i )res = min(res ,f[m][i]);
if(res == INF)res = -1;
printf("Case %d: %d\n",T++,res);
}
return 0;
}