Max Sum Plus Plus
状态表示f(i,j),表示在i个组的情况下选则j物品的最大值;
转移方程:f[i][j]=max(f[i][j-1]+a[j],max(f[i-1][k]+a[j])
因为每次用到的都是f[i-1][0~j-1],可以使用一个数组把这个结果存储起来;
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 1e5 + 10;
typedef long long ll;
ll a[N], f[N], max_j[N];//max—_j存储i-1中的最大值;
const int INF =0x7fffffff;
void solve() {
int m, n;
ll mmax = 0;
while (scanf("%d%d", &m, &n) != EOF) {
memset(max_j,0,sizeof max_j);
memset(f,0,sizeof f);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
for (int i = 1; i <= m; i++) {
mmax = -INF;
for (int j = i; j <= n; j++) {//从i开始枚举,因为存在j比i小的分组;
f[j] = max(f[j - 1] + a[j], max_j[j - 1] + a[j]);
max_j[j - 1] = mmax;
mmax = max(mmax, f[j]);
}
}
printf("%lld\n", mmax);
}
}
int main() {
solve();
return 0;
}
Ignatius and the Princess IV
这个题完全没必要dp,dp专题就写个dp
f[n],前n个数字中出现次数最多的数字,
状态分析f[i]=f[i-1],a[i]出现次数取max;
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;
const int N = 1e6 + 10;
int a[N], f[N];
void solve() {
int n;
map<int, int> m;
while (~scanf("%d", &n)) {
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), m[a[i]]++;
for (int i = 1; i <= n; i++) {
f[i] = m[f[i - 1]] > m[a[i]] ? f[i - 1] : a[i];
}
m.clear();
printf("%d\n", f[n]);
}
}
int main() {
solve();
return 0;
}
Monkey and Banana
LIS模型;
状态表示:f[i],以i结尾的最大高度;
状态转移:f[i]=max(f[i],f[j]+a[i].z);
每个矩形通过变换可以变成6种,所以一共又6*N个矩形;
首先按照长和宽的大小排序,然后就是LIS的dp模型;
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 200;
int len;
struct node {
int x, y, z;
} a[N];
int f[N];
void add(int x, int y, int z) { //每个矩形都能变化成6种;
a[len].x = x, a[len].y = y, a[len].z = z, len++;
a[len].x = y, a[len].y = x, a[len].z = z, len++;
a[len].x = z, a[len].y = x, a[len].z = y, len++;
a[len].x = x, a[len].y = z, a[len].z = y, len++;
a[len].x = y, a[len].y = z, a[len].z = x, len++;
a[len].x = z, a[len].y = y, a[len].z = x, len++;
}
bool cmp(node a, node b) {
if (a.x == b.x) return a.y > b.y;
return a.x > b.x;
}
void solve() {
int n;
int x, y, z;
int Case = 1;
while (scanf("%d", &n) and n) {
int mmax = 0;
memset(f, 0, sizeof f);
len = 0;
for (int i = 1; i <= n; i++) {
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
}
sort(a , a + len, cmp);
for (int i = 0; i < len; i++) {
f[i] = a[i].z;
for (int j = 0; j < i; j++) {
if (a[j].x > a[i].x and a[j].y > a[i].y) {
f[i] = max(f[i], f[j] + a[i].z);
mmax = max(f[i], mmax);
}
}
}
printf("Case %d: maximum height = %d\n", Case++, mmax);
}
}
int main() {
solve();
return 0;
}
Super Jumping! Jumping! Jumping!
LIS模型
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1010;
int a[N], f[N];
void solve() {
int n;
int res = 0;
while (cin >> n, n) {
res = 0;
memset(f, 0, sizeof f);
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) {
f[i] = a[i];
for (int j = 1; j < i; j++) {
if (a[j] < a[i]) f[i] = max(f[i], f[j] + a[i]);
res = max(res, f[i]);
}
}
cout << res << endl;
}
}
int main() {
solve();
return 0;
}
最少拦截系统
LIS模型:只要有上升序列的一定需要多个拦截系统;
#include <iostream>
#include <algorithm>
#include <map>
#include <cstring>
using namespace std;
const int N = 1010;
int f[N], a[N];
void solve() {
int n;
while (~scanf("%d", &n)) {
memset(f, 0, sizeof f);
int mmax = 0;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) {
f[i] = 1;
for (int j = 1; j < i; j++) {
if (a[i] > a[j]) f[i] = max(f[i], f[j] + 1);
mmax = max(mmax, f[i]);
}
}
printf("%d\n", mmax);
}
}
int main() {
solve();
return 0;
}
Piggy-Bank
完全背包模型,取最小值一定要初始化和考虑边界;
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1010;
int w[N], p[N], dp[10000];
void solve() {
int e, f;
int n;
scanf("%d%d%d", &e, &f, &n);
int m = f - e;
for (int i = 1; i <= n; i++) {
scanf("%d%d", &p[i], &w[i]);
}
memset(dp, 0x3f, sizeof dp);
dp[0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = w[i]; j <= m; j++) {
dp[j] = min(dp[j], dp[j - w[i]] + p[i]);
}
}
if (dp[m] != 0x3f3f3f3f) printf("The minimum amount of money in the piggy-bank is %d.\n", dp[m]);
else printf("This is impossible.\n");
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
免费馅饼
线性dp,数字三角形模型:
状态表示 :f[i][j],第i秒j位置可以取到的最大值;因为不能够明确最后会在哪个位置取得最大值,所以利用桶排可以倒着跑;
转移方程 f[i][j]=max(f[i+1][j],f[i+1][j-1],f[i+1][j+1]);
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 100010;
int g[N][20], f[N][20];
void solve() {
int n;
while (scanf("%d", &n) and n) {
int t, x;
memset(f, 0, sizeof f);
memset(g, 0, sizeof g);
int maxt = 0;
for (int i = 0; i < n; i++) {
scanf("%d%d", &x, &t);
g[t][x]++;
maxt = max(maxt, t);
}
for (int i = maxt; i >= 0; i--) {
for (int j = 0; j < 11; j++) {
f[i][j] = max(f[i + 1][j], max(f[i + 1][j + 1], f[i + 1][j - 1])) + g[i][j];
}
}
printf("%d\n", f[0][5]);
}
}
int main() {
solve();
return 0;
}
解题心得:首先要不重不漏的找出一个可以表示答案的集合;
然后想暴力方法的做法,一半就是按照这个方法的循环嵌套模式;
集合划分,一定不能漏掉任何一个,然后找到他们之间的关系,转移方程大致上就写出来啦^^=
附加:数字三角形
状态表示: f[i][j][k],k表示向左走的步数;
一共有n层,则会走n-1步;
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int g[N][N], f[N][N][N];
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++)
cin >> g[i][j];
}
for (int i = n ; i >= 1; i--) {
for (int j = 1; j <= i; j++) {
for (int k = 0; k <= n - i; k++) {
f[i][j][k] = max(f[i + 1][j][k - 1] , f[i + 1][j + 1][k]) + g[i][j];
}
}
}
if (n % 2 == 1) {
cout << f[1][1][(n - 1) / 2] << endl;
} else cout << max(f[1][1][(n - 1) / 2], f[1][1][(n - 1) / 2 + 1]) << endl;
}
int main() {
solve();
return 0;
}
Tickets
//输出比转移方程更难系列qwq
转移方程
f[i]=min(f[i-1+a[i],f[i-2]+b[i-1])
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 2010;
int f[N];
int a[N], b[N];
void solve() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
if (n != 1) {
for (int i = 1; i < n; i++) scanf("%d", &b[i]);
}
memset(f, 0x3f, sizeof f);
f[0] = 0;
for (int i = 1; i <= n; i++) {
f[i] = f[i - 1] + a[i];
if (i >= 2) {
f[i] = min(f[i], f[i - 2] + b[i - 1]);
}
}
int res = f[n];
int h = res / 3600 + 8;
int m = (res % 3600) / 60;
int s = (res % 3600) % 60;
char noon = 'a';
if (h > 12) {
noon = 'p';
h -= 4;
}
int one = h / 10;
int two = h % 10;
int thr = m / 10;
int fou = m % 10;
int fiv = s / 10;
int six = s % 10;
printf("%d%d:%d%d:%d%d %cm\n", one, two, thr, fou, fiv, six, noon);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
FatMouse’s Speed
LIS+记录路径
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <stack>
using namespace std;
const int N = 1010;
struct node {
int w, s;
int pos;
} a[N];
int f[N];
int tem[N];
bool cmp(node a, node b) {
if (a.w == b.w) return a.s > b.s;
return a.w < b.w;
}
void solve() {
int n = 1;
int mmax = 0;
while (~scanf("%d%d", &a[n].w, &a[n].s)) {
a[n].pos = n;
n++;
}
sort(a + 1, a + n, cmp);
for (int i = 1; i < n; i++) {//LIS;
f[i] = 1;
tem[i] = 0;
for (int j = 1; j < i; j++) {
if (a[j].w <= a[i].w and a[j].s >= a[i].s) {
if (f[i] < f[j] + 1) {
f[i] = f[j] + 1;
tem[i] = j;
}
mmax = max(mmax, f[i]);
}
}
}
printf("%d\n", mmax);
int k = 1;
for (int i = 1; i <= n; i++)//记录路径
if (f[k] < f[i]) f[k] = f[i], k = i;
int len = f[k];
stack<int> S;
for (int i = 1; i <= len; i++) {
S.push(a[k].pos);
k = tem[k];
}
while (S.size()) {
printf("%d\n", S.top());
S.pop();
}
}
int main() {
solve();
return 0;
}