美丽序列
#include <string.h>
#include <iostream>
#define Mod 1000000007
using namespace std;
int main() {
int n;
long long a[42];
long long dp[42][42][3][1602];
// dp[i][j][1][k]代表当前 处理到第i个且值为j 在递减序列中第1个 前i个和为k
memset(dp, 0, sizeof(dp));
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
//初始化
if (a[1] == -1) {
for (int i = 0; i <= 40; i++)
dp[1][i][1][i] = 1;
} else
dp[1][a[1]][1][a[1]] = 1;
for (int i = 2; i <= n; i++) {
if (a[i] == -1) { //若当前数为 -1 即可为任何数
for (int j = 0; j <= 40; j++) { //枚举当前可能的数 0~40
for (int L = 0; L <= 40; L++) { //枚举当前前一个(i-1)可能的数 0~40
for (int k = j * (i - 1); k <= 1600 - j; k++) { //枚举前(i-1)个满足条件的和k
if (j >= L) { //若当前大于前一个数 即打破递减序列的条件
dp[i][j][1][k + j] = (dp[i][j][1][k + j] + dp[i - 1][L][1][k]) % Mod;
dp[i][j][1][k + j] = (dp[i][j][1][k + j] + dp[i - 1][L][2][k]) % Mod;
} else
dp[i][j][2][k + j] = (dp[i][j][2][k + j] + dp[i - 1][L][1][k]) % Mod;
}
}
}
}
else { //若为具体的大小
for (int L = 0; L <= 40; L++) { //枚举上一个数的大小
for (int k = a[i] * (i - 1); k <= 1600 - a[i]; k++) { //枚举前(i-1)个满足条件的和k
if (a[i] >= L) {
dp[i][a[i]][1][k + a[i]] = (dp[i][a[i]][1][k + a[i]] + dp[i - 1][L][1][k]) % Mod;
dp[i][a[i]][1][k + a[i]] = (dp[i][a[i]][1][k + a[i]] + dp[i - 1][L][2][k]) % Mod;
} else
dp[i][a[i]][2][k + a[i]] = (dp[i][a[i]][2][k + a[i]] + dp[i - 1][L][1][k]) % Mod;
}
}
}
}
long long sum = 0;
for (int j = 0; j <= 40; j++) { //枚举 可能的大小
for (int k = j * n; k <= 1600; k++) { //枚举可能的和
sum = (sum + dp[n][j][1][k]) % Mod;
//当前数大小为j且在递减位置1 和为k的美丽序列数
sum = (sum + dp[n][j][2][k]) % Mod;
}
}
cout << sum << endl;
return 0;
}
题解:就是dp,把有关的变量都适当列为dp数组的其中一维,然后反复推敲模拟。题解链接
1027. 方格取数
#include <bits/stdc++.h>
using namespace std;
const int N = 15;
int n;
int w[N][N];
int f[N * 2][N][N];
int main() {
scanf("%d", &n);
int a, b, c;
while (cin >> a >> b >> c, a || b || c)
w[a][b] = c;
for (int k = 2; k <= n + n; k++) {
for (int i1 = 1; i1 <= n; i1++) {
for (int i2 = 1; i2 <= n; i2++) {
int j1 = k - i1, j2 = k - i2;
if (j1 >= 1 && j1 <= n && j2 >= 1 && j2 <= n) {
int t = w[i1][j1];
if (i1 != i2)
t += w[i2][j2];
int& x = f[k][i1][i2];
x = max(x, f[k - 1][i1 - 1][i2 - 1] + t);
x = max(x, f[k - 1][i1 - 1][i2] + t);
x = max(x, f[k - 1][i1][i2 - 1] + t);
x = max(x, f[k - 1][i1][i2] + t);
}
}
}
}
printf("%d\n", f[n + n][n][n]);
return 0;
}
3996. 涂色
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 5010;
int n;
int c[N], f[N][N];
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &c[i]);
if (i && c[i] == c[i - 1]) {
i--;
n--;
}
}
for (int len = 2; len <= n; len++)
for (int l = 0; l + len - 1 < n; l++) {
int r = l + len - 1;
if (c[l] != c[r])
f[l][r] = min(f[l + 1][r], f[l][r - 1]) + 1;
else
f[l][r] = f[l + 1][r - 1] + 1;
}
printf("%d\n", f[0][n - 1]);
return 0;
}
题解:区间dp
283. 多边形
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 110;
int n;
int p[M];
char c[M], ca;
int dp[M][M], dmin[M][M];
int w[M];
int main() {
cin >> n;
memset(dp, -0x3f, sizeof(dp));
memset(dmin, 0x3f, sizeof(dmin));
for (int i = 1; i <= n * 2; ++i) {
if (i == 1) {
getchar();
scanf("%c", &c[n]);
} else if (i & 1) {
getchar();
scanf("%c", &c[i / 2]);
c[i / 2 + n] = c[i / 2];
} else {
scanf("%d", &p[i / 2]);
dp[i / 2][i / 2] = dp[i / 2 + n][i / 2 + n] = p[i / 2 + n] =
p[i / 2];
dmin[i / 2][i / 2] = dmin[i / 2 + n][i / 2 + n] = p[i / 2];
}
}
for (int len = 2; len <= n; ++len) {
for (int i = 1; i + len - 1 <= n * 2; ++i) {
int j = i + len - 1;
for (int k = i; k < j; ++k) {
if (c[k] == 't') {
dp[i][j] = max(dp[i][k] + dp[k + 1][j], dp[i][j]);
dmin[i][j] = min(dmin[i][k] + dmin[k + 1][j], dmin[i][j]);
} else {
dp[i][j] = max(dp[i][k] * dp[k + 1][j], dp[i][j]);
dp[i][j] = max(dmin[i][k] * dmin[k + 1][j], dp[i][j]);
dmin[i][j] = min(dp[i][k] * dmin[k + 1][j], dmin[i][j]);
dmin[i][j] = min(dmin[i][k] * dmin[k + 1][j], dmin[i][j]);
dmin[i][j] = min(dmin[i][k] * dp[k + 1][j], dmin[i][j]);
}
}
}
}
int maxv = -0x7f7f7f7f, len = 0;
for (int i = 1; i <= n; ++i) {
if (dp[i][i + n - 1] > maxv) {
maxv = dp[i][i + n - 1];
len = 0;
w[len++] = i;
} else if (dp[i][i + n - 1] == maxv) {
w[len++] = i;
}
}
cout << maxv << endl;
for (int i = 0; i < len; ++i)
cout << w[i] << " ";
cout << endl;
return 0;
}
题解:区间dp,要注意的点就是题目给的数字有负数,众所周知 因为负负得正,可能在某些时刻需要用最小值乘最小值来得到一个最大正值,所以我们求最大值的同时还得求一下最小值。