状态机模型
AcWing 1049. 大盗阿福
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <ctime>
#include <sstream>
#define endl '\n'
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define lowbit(x) (x&-x)
using namespace std;
const double pi = acos(-1);
typedef long long ll;
typedef pair<int, int> PII;
typedef pair<long, long> PLL;
const int N = 1e5 + 10;
int w[N];
int f[N][2];
int main()
{
int _; cin >> _;
while (_ -- )
{
int n;
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> w[i];
// memset(f, 0, sizeof f);
for (int i = 1; i <= n; i ++ )
{
f[i][0] = max(f[i - 1][0], f[i - 1][1]);
f[i][1] = f[i - 1][0] + w[i];
}
cout << max(f[n][0], f[n][1]) << endl;
}
}
AcWing 1057. 股票买卖 IV (初始化)
- 本题限制了 总交易的次数,因此不能用贪心
- 对于第 i i i天购入的股票,当且仅当第 j ( i < = j ) j(i<=j) j(i<=j)天才能卖掉,获得的利润是 w j − w i w_j-w_i wj−wi,由此可知这是一个线性问题
- 以当前递推到的天数,作为线性dp的阶段,对于递推到的第 i i i天,我们需要记录的信息有:
- 1.在完成第 i i i天的决策后,状态是持仓还是空仓
- 2.在第 i i i天交易完成时,总共完成的完整的交易数(题目规定一次买入加一次卖出才是一场完整交易)
- 于是,状态表示 f [ i , j , k ] f[i,j,k] f[i,j,k]表示考虑前 i i i天的股票,第 i i i天的持仓空仓状态是 k k k,且完成的完整交易次数是 j j j,属性是 m a x max max
状态机模型DP
- 1.第i天状态是空仓状态(i==0),则第i2天产生的行为是卖出行为或者空仓行为
- 2.第i天状态是持仓状态(i==1),则第i天产生的行为是买入行为或者持仓行为
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <ctime>
#include <sstream>
#define endl '\n'
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define lowbit(x) (x&-x)
using namespace std;
const double pi = acos(-1);
typedef long long ll;
typedef pair<int, int> PII;
typedef pair<long, long> PLL;
const int N = 1e5 + 10, M = 110;
int f[N][M][2];
int w[N];
int main()
{
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; i ++ ) cin >> w[i];
// 注意1
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 = 0; j <= k; j ++ )
{
f[i][j][0] = f[i - 1][j][0];
if (j) f[i][j][0] = max(f[i][j][0], f[i - 1][j - 1][1] + w[i]); // 注意2
f[i][j][1] = max(f[i - 1][j][1], f[i - 1][j][0] - w[i]);
}
int res = 0;
for (int i = 0; i <= k; i ++ ) res = max(res, f[n][i][0]); // 注意3 最后一天必须是空仓
cout << res << endl;
return 0;
}
AcWing 1058. 股票买卖 V
- 状态 f [ i , j ] f[i,j] f[i,j]表示考虑前i天股市,当前第i天的状态是j的方案,属性是 m a x max max
- 如果第i天是空仓(且非冷冻)状态(0),则第i-1天是空仓或者即将冷冻 即 f [ i , 0 ] = m a x ( f [ i − 1 , 0 ] , f [ i − 1 , 2 ] ) f[i,0]=max(f[i-1,0],f[i-1,2]) f[i,0]=max(f[i−1,0],f[i−1,2])
- 如果第i天是即将冷冻状态(2),则第i-1天是持仓状态 即 f [ i , 2 ] = f [ i − 1 , 1 ] + w [ i ] f[i,2]=f[i-1,1]+w[i] f[i,2]=f[i−1,1]+w[i]
- 如果第i天是持仓状态(1),则第i-1天是持仓状态或者空仓 即 f [ i , 1 ] = m a x ( f [ i − 1 , 1 ] , f [ i − 1 , 0 ] − w [ i ] ) f[i,1]=max(f[i-1,1],f[i-1,0]-w[i]) f[i,1]=max(f[i−1,1],f[i−1,0]−w[i])
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <ctime>
#include <sstream>
#define endl '\n'
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define lowbit(x) (x&-x)
using namespace std;
const double pi = acos(-1);
typedef long long ll;
typedef pair<int, int> PII;
typedef pair<long, long> PLL;
const int N = 1e5 + 10;
int w[N];
int f[N][3];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> w[i];
memset(f, -0x3f, sizeof f);
f[0][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][1], f[i - 1][0] - w[i]);
f[i][2] = f[i - 1][1] + w[i];
}
cout << max(f[n][0], f[n][2]) << endl;
return 0;
}
AcWing 1052. 设计密码
- 这题是有 ∣ T ∣ + 1 |T|+1 ∣T∣+1个状态自动机
- m个点,每个点有26条边。一个字符串不包含某个字符串,其实是表示在kmp中走不到m这个点,可以把kmp的过程看成状态机跳的过程(走到最后一个点表示匹配了,我们要不匹配,所以就是走不到最后一个点)的方案数
- f [ i , j ] f[i,j] f[i,j]表示当前已经填到了字符串的第i位(一共填n位),j表示走到了kmp中第j个位置。那么状态数量就有N*M,每个状态有26种枚举方式。因此 26 N 2 26N^2 26N2。建图时, M ∗ 26 M*26 M∗26(每个状态有26种选择) ∗ N *N ∗N(每个选择有一个while循环),所以预处理也是 26 N 2 26N^2 26N2,因此总的就是 26 N 2 26N^2 26N2
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <string.h>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <ctime>
#include <sstream>
#define endl '\n'
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define lowbit(x) (x&-x)
using namespace std;
const double pi = acos(-1);
typedef long long ll;
typedef pair<int, int> PII;
typedef pair<long, long> PLL;
const int N = 55, mod = 1e9 + 7;
int n, m;
char str[N];
int f[N][N];
int ne[N];
int main()
{
cin >> n >> (str + 1);
m = strlen(str + 1);
for (int i = 2, j = 0; i <= n; i ++ )
{
while (j && str[i] != str[j + 1]) j = ne[j];
if (str[i] == str[j + 1]) j ++ ;
ne[i] = j;
}
f[0][0] = 1;
for (int i = 0; i < n; i ++ ) // 最终串的长度
for (int j = 0; j < m; j ++ ) // kmp数组的长度
for (char k = 'a'; k <= 'z'; k ++ ) // 当前位置选哪个字母
{
int u = j; // j需要跳到哪
while (u && k != str[u + 1]) u = ne[u];
if (k == str[u + 1]) u ++ ;
if (u < m) f[i + 1][u] = (f[i + 1][u] + f[i][j]) % mod; // u小于m就表示不匹配,那么是可以走的
}
int res = 0;
for (int i = 0; i < m; i ++ ) res = (res + f[n][i]) % mod; // 累加所有走不到m的状态
cout << res << endl;
return 0;
}
AcWing 1053. 修复DNA
- f [ i , j ] f[i,j] f[i,j]表示前i个字母,当前走到了ac自动机中第j个位置的所有的操作方案中最少修改的字母数量
- 建trie时要在所有结尾的地方打一个标记,然后再将trie建成ac自动机。所有打上标记的点应该是完全避开的
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <string.h>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <ctime>
#include <sstream>
#define endl '\n'
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define lowbit(x) (x&-x)
using namespace std;
const double pi = acos(-1);
typedef long long ll;
typedef pair<int, int> PII;
typedef pair<long, long> PLL;
const int N = 1010;
int n, m;
int tr[N][4], dar[N], idx;
int q[N], ne[N];
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 t = get(str[i]);
if (tr[p][t] == 0) tr[p][t] = ++ idx;
p = tr[p][t];
}
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[ne[t]][i];
else
{
ne[p] = tr[ne[t]][i];
q[ ++ tt] = p;
dar[p] |= dar[ne[p]];
}
}
}
}
int main()
{
int T = 1;
while (cin >> n, n)
{
memset(tr, 0, sizeof tr);
memset(dar, 0, sizeof dar);
memset(ne, 0, sizeof ne);
idx = 0;
for (int i = 0; i < n; i ++ )
{
cin >> str;
insert();
}
build();
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 = 0x3f3f3f3f;
for (int i = 0; i <= idx; i ++ ) res = min(res, f[m][i]);
if (res == 0x3f3f3f3f) res = -1;
printf("Case %d: %d\n", T ++ , res);
}
}