2019年杭电多校第一场 1001题blank(DP)HDU6578
- 解决思路,开一个DP数组来存储0 1 2 3四个字符最后出现的位置,并且在DP中已经==排好序==。
- DP开四维,DP[i][j][k][2],最后一位开==滚动数组==,用来记录上一个字符串长度时各个位置信息(及滚动继承),i,j,k分别记录==三个字符==的位置,因为无论在什么情况下,必定有一位它最后的位置和字符串的长度相等,所以不用存在dp中,dp只用存三个字符的最后位置。
- (注意,i,j,k只是按从小到大存储了字符最后出现的位置,而没有指定是哪个字符)
- dp状态转移方程:
- 解释:假设当前,0最后出现在i的位置,1最后出现在j的位置,2最后出现在k的位置,3最后出现在==字符串长度==(cur)的位置。在下一次新的情况中,当后一位加上0时,那么按照顺序新3的最后出现的位置为cur-1(即上一个字符串的长度),其余不变,按从小到大,顺序为dp[j][k][cur - 1][now],然后继承上一个。以此类推。
- 关于限制条件,用一个vector存入,当发现不符合时候,讲对应的DP置换成0。最后符合长度N的所有DP相加即为答案。
解决思路,开一个DP数组来存储0 1 2 3四个字符最后出现的位置,并且在DP中已经排好序。
DP开四维,DP[i][j][k][2],最后一位开滚动数组,用来记录上一个字符串长度时各个位置信息(及滚动继承),i,j,k分别记录三个字符的位置,因为无论在什么情况下,必定有一位它最后的位置和字符串的长度相等,所以不用存在dp中,dp只用存三个字符的最后位置。
(注意,i,j,k只是按从小到大存储了字符最后出现的位置,而没有指定是哪个字符)
dp状态转移方程:
dp[i][j][k][now] += dp[i][j][k][last]; // now为当前状态,last为上一次的状态
dp[j][k][cur - 1][now] += dp[i][j][k][last];
dp[i][k][cur - 1][now] += dp[i][j][k][last];
dp[i][j][cur - 1][now] += dp[i][j][k][last];
解释:假设当前,0最后出现在i的位置,1最后出现在j的位置,2最后出现在k的位置,3最后出现在字符串长度(cur)的位置。在下一次新的情况中,当后一位加上0时,那么按照顺序新3的最后出现的位置为cur-1(即上一个字符串的长度),其余不变,按从小到大,顺序为dp[j][k][cur - 1][now],然后继承上一个。以此类推。
关于限制条件,用一个vector存入,当发现不符合时候,讲对应的DP置换成0。最后符合长度N的所有DP相加即为答案。
代码:
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
const int mo = 998244353;
const int N = 107;
int dp[N][N][N][2];
vector<pair<int, int> > d[N];
int main()
{
int t;
scanf("%d", &t);
while (t--) {
int n, m;
scanf("%d%d", &n, &m);
int l, r, x;
for (int i = 1; i <= n; i++) {
d[i].clear();//d[i].push_back(make_pair(i, 1));
}
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &l, &r, &x);
d[r].push_back(make_pair(l, x));
}
memset(dp, 0, sizeof(dp));
dp[0][0][0][0] = 1;
for (int cur = 1; cur <= n; cur++) {
int now = cur & 1;
int last = now ^ 1;
for (int i = 0; i <= cur; i++)
for (int j = i; j <= cur; j++)
for (int k = j; k <= cur; k++)
dp[i][j][k][now] = 0;
//i<j<k;
for (int i = 0; i <= cur; i++)
for (int j = i; j <= cur; j++)
for (int k = j; k <= cur; k++) {
dp[i][j][k][now] += dp[i][j][k][last];
dp[j][k][cur - 1][now] += dp[i][j][k][last];
dp[i][k][cur - 1][now] += dp[i][j][k][last];
dp[i][j][cur - 1][now] += dp[i][j][k][last];
dp[i][j][k][now] %= mo;
dp[j][k][cur - 1][now] %= mo;
dp[i][k][cur - 1][now] %= mo;
dp[i][j][cur - 1][now] %= mo;
}
for (int i = 0; i <= cur; i++)
for (int j = i; j <= cur; j++)
for (int k = j; k <= cur; k++)
for (auto re : d[cur]) { //遍历d这个数组,写了一个类似标程的写法
l = re.first, r = cur, x = re.second;
if (( (i >= l) + (j >= l) + (k >= l) + (cur >= l) ) != x)
dp[i][j][k][now] = 0;
}
}
int ans= 0;
for (int i = 0; i <= n; i++)
for (int j = i; j <= n; j++)
for (int k = j; k <= n; k++)
(ans += dp[i][j][k][n & 1]) %= mo;
printf("%d\n", ans%mo);
}
return 0;
}