基础dp专题训练

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值