[kuangbin带你飞]专题十二 基础dp1

来源:互联网 发布:算法 pdf 高清 编辑:程序博客网 时间:2024/06/06 18:50

Max Sum Plus Plus

题意:

  给你一个序列n个数组成,然后让你在里面找到m个字序列,让这不重叠的m个子序列的和最大。

思路:

  这题比较水,所以不需要考虑太多数据相关的东西。直接二维dpOrz。

  dp[i][j]:前j个序列,以i为结尾的,最大子序列和。dp[i][j]=max(dp[i - 1][j], dp[k][j - 1])+a[i]。即第i个数放在第j个序列里或者第j-1个序列里。显然k<i。显然我们可以拿一个数组来优化一下for1...k这个循环,做到O(1)。

#include <bits/stdc++.h>using namespace std;const int maxn = 1000000 + 5;int a[maxn];long long dp[maxn];long long maxx[maxn];int main(){    int m, n;    while(~scanf("%d%d", &m, &n))    {        memset(maxx, 0, sizeof(maxx));        for(int i = 1; i <= n; i++)            scanf("%d", &a[i]);       long long temp;        for(int i = 1; i <= m; i++)        {            temp = -1e18;            for(int j = i; j <= n; j++)            {                dp[j] = max(dp[j - 1], maxx[j - 1]) + a[j];                maxx[j - 1] = temp;                temp = max(temp, dp[j]);            }        }        printf("%lld\n", temp);    }    return 0;}


Ignatius and the Princess IV

思路:

  water


Monkey and Banana

题意:

  把给定的长方体(不限)叠加在一起,叠加的条件是,上面一个长方体的长和宽都比下面长方体的长和宽短;求这些长方体能叠加的最高的高度.

思路:

  其实这个问题可以转化成DAG上的一个最长路。长宽高三个维度,可以投射出六种(a, b ,c),当长宽满足其中要求时,连边,我们就可以得到一个DAG,然后再这个DAG上dp或者搜索出一条最长路就行了。

        wa点:应该把所有的的点都初始化,而不只是初始化最大的,返利20 30 30。       

#include <bits/stdc++.h>using namespace std;const int INF = 0x3f3f3f3f;const int maxn = 3e3 + 5;struct node{    int x, y, z;    bool operator<(const node &other)const    {        if(x != other.x)    return x > other.x;        if(y != other.y)    return y > other.y;        return z < other.z;    }};vector<node>vec;int dp[maxn];int main(){    int n, cas = 1;    while(~scanf("%d", &n))    {        if(n == 0)  break;        vec.clear();        for(int i = 0; i < n; i++)        {            int x, y, z;            scanf("%d%d%d", &x, &y, &z);            vec.push_back({x, y, z});            vec.push_back({y, x, z});            vec.push_back({x, z, y});            vec.push_back({z, x, y});            vec.push_back({z, y, x});            vec.push_back({y, z, x});        }        sort(vec.begin(), vec.end());        for(int i = 0; i < vec.size(); i++) dp[i] = vec[i].z;        for(int i = 0; i < vec.size(); i++)        {            for(int j = i + 1; j <vec.size();j++)            {                if(vec[j].x < vec[i].x &&  vec[j].y < vec[i].y)                {                    dp[j] = max(dp[j], dp[i] + vec[j].z);                }            }        }        int ans = -INF;        for(int i = 0; i < vec.size(); i++) ans = max(ans, dp[i]);        printf("Case %d: maximum height = %d\n", cas++, ans);    }    return 0;}

Doing Homework

题意:

  有n门课,每门课有截止时间和完成所需的时间,如果超过规定时间完成,每超过一天就会扣1分,问怎样安排做作业的顺序才能使得所扣的分最小。

思路:

        我们可以看到数据范围是15,所以显然是一个状压dp。记录一下路径

#include <bits/stdc++.h>using namespace std;const int maxn = (1 << 16);const int INF = 0x3f3f3f3f;char name[20][105];int ddl[20], cost[20];int t[maxn], pre[maxn], dp[maxn];void show(int x){    if(x == 0)  return;    show(x - (1 << pre[x]));    printf("%s\n", name[pre[x]]);}int main(){    int T;    scanf("%d", &T);    while(T--)    {        int n;        scanf("%d", &n);        for(int i = 0; i < n; i++)        {            scanf("%s%d%d", name[i], &ddl[i], &cost[i]);        }        for(int S = 1; S < (1 << n); S++)        {            dp[S] = INF;            for(int i = n - 1; i >= 0; i--)            {                if((S & (1 << i)) == 0)   continue;                int past = S - (1 << i);                int add = t[past] + cost[i] - ddl[i];                if(add < 0) add = 0;                if(dp[past] + add < dp[S])                {                    dp[S] = dp[past] + add;                    t[S] = t[past] + cost[i];                    pre[S] = i;                }            }        }        int o = (1 << n) - 1;        printf("%d\n", dp[o]);        show(o);    }    return 0;}


Super Jumping! Jumping! Jumping!

思路:  

  裸的的LIS


Piggy-Bank

题意:

  给金币的面额和重量,求在满足一定重量时的最小价值。

思路:

        完全背包

#include <bits/stdc++.h>using namespace std;const int maxn = 500 + 5;const int INF = 0x3f3f3f3f;typedef long long LL;int dp[10000 + 5];int val[maxn], w[maxn];int main(){    int T;    scanf("%d", &T);    while(T--)    {        memset(dp, INF, sizeof(dp));        int x, y, n;        scanf("%d%d%d", &x, &y, &n);        int V = y - x;        for(int i = 1; i <= n; i++)        {            scanf("%d%d", &val[i], &w[i]);        }        dp[0] = 0;        for(int i = 1; i <= n; i++)        {            for(int j = w[i]; j <= V; j++)            {                dp[j] = min(dp[j], dp[j - w[i]] + val[i]);            }        }        if(dp[V] >= INF)        {            puts("This is impossible.");        }        else        {            printf("The minimum amount of money in the piggy-bank is %d.\n", dp[V]);        }    }    return 0;}


免费馅饼

题意:

        有一个数轴,从0到10,小明开始在5这个位置。现在天上开始掉馅饼,小明每次只能移动单位一的长度,求小明最多能接到多少馅饼。

思路:

        简单dp。

#include <bits/stdc++.h>using namespace std;const int INF = 0x3f3f3f3f;const int maxn = 100000 + 5;int cake[15][maxn];int dp[15][maxn];int main(){    int n;    while(~scanf("%d", &n))    {        if(n == 0)  break;        memset(cake, 0, sizeof(cake));        memset(dp, -INF, sizeof(dp));//注意一个 初始位置在5的初始化        dp[5][0] = 0;        int maxT = 0;        for(int i = 0; i < n; i++)        {            int x, time;            scanf("%d%d", &x, &time);            cake[x][time]++;            maxT = max(maxT, time);        }        for(int j = 1; j <= maxT; j++)        {            for(int i = 0; i <= 10; i++)            {                if(i - 1 >= 0)   dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + cake[i][j]);                if(i + 1 <= 10)  dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + cake[i][j]);                dp[i][j] = max(dp[i][j], dp[i][j - 1] + cake[i][j]);            }        }        int ans = 0;        for(int i = 0; i <= 10; i++)        {            ans = max(ans, dp[i][maxT]);        }        printf("%d\n", ans);    }    return 0;}


Tickets

题意:

  有k个人要买票(你也在里面最后一位),每个人买票要花一个时间ai,或者两个相邻的人一起花费一个时间bi, 问你最少花费多少时间才能买到票。

思路:

        简单dp,再开一个维度记录一下是第i次购买单人票和双人票的情况就行了。

#include <bits/stdc++.h>using namespace std;const int maxn = 2000 + 5;const int INF = 0x3f3f3f3f;int t[maxn], adj[maxn];int dp[maxn][2];int main(){    int T;    scanf("%d", &T);    while(T--)    {        memset(dp, INF, sizeof(dp));        int n;        scanf("%d", &n);        for(int i = 1; i <= n; i++)        {            scanf("%d", &t[i]);        }        for(int i = 2; i <= n; i++)        {            scanf("%d", &adj[i]);        }        dp[0][0] = dp[0][1] = 0;        for(int i = 1; i <= n; i++)        {            dp[i][0] = min(dp[i - 1][0], dp[i - 1][1]) + t[i];            if(i - 2 >= 0)            {                dp[i][1] = min(dp[i - 2][0], dp[i - 2][1]) + adj[i];            }        }        int ans = min(dp[n][0], dp[n][1]);        int hh = 8, mm = 0, ss = 0;        hh += ans / 3600;        ans %= 3600;        mm += ans / 60;        ss += ans % 60;        printf("%02d:%02d:%02d %s\n", hh, mm, ss, hh >= 12 ? "pm" : "am");    }    return 0;}

最少拦截系统

思路:

  LCS。


FatMouse's Speed

题意:

  找到一个最多的老鼠序列,使得序列中的老鼠的体重满足递增,相应老鼠的速度满足递减。

思路:

  按某一维排序以后,直接LIS就行了。但是这题窝一定要吐槽!!!! 窝日 为什么按w升序就行,降序就不行exome?

#include <bits/stdc++.h>using namespace std;const int maxn = 1e3 + 5;const int INF = 0x3f3f3f3f;typedef long long LL;typedef pair<int, int>pii;int path[maxn];int dp[maxn];struct node{    int s, w, id;    bool operator < (const node &other)const    {        if(w != other.w) return w < other.w;        return s > other.s;    }}m[maxn];;int main(){    int s, w, cnt = 0;    while(~scanf("%d%d", &s, &w))    {        m[cnt] = {s, w, cnt + 1};        cnt++;    }    sort(m, m + cnt);    memset(path, -1, sizeof(path));    for(int i = 1; i < cnt; i++)    dp[i] = 1;    for(int i = 1; i < cnt; i++)    {        for(int j = 0; j < i; j++)        {            if(m[j].w < m[i].w && m[j].s > m[i].s && dp[i] < dp[j] + 1)            {                dp[i] = dp[j] + 1;                path[m[i].id] = m[j].id;            }        }    }    int ans = dp[0], pos = m[0].id;    for(int i = 0; i < cnt; i++)    {        if(dp[i] > ans)        {            ans = dp[i];            pos = m[i].id;        }    }    printf("%d\n", ans);    queue<int>que;    que.push(pos);    while(path[pos] != -1)    {        que.push(path[pos]);        pos = path[pos];    }    while(que.size())    {        printf("%d\n", que.front());        que.pop();    }    return 0;}



Common Subsequence

思路:

  裸的LCS。


Help Jimmy

题意:

  场景中包括多个长度和高度各不相同的平台。地面是最低的平台,高度为零,长度无限。  Jimmy老鼠在时刻0从高于所有平台的某处开始下落,它的下落速度始终为1米/秒。当Jimmy落到某个平台上时,游戏者选择让它向左还是向右跑,它跑动的速度也是1米/秒。当Jimmy跑到平台的边缘时,开始继续下落。Jimmy每次下落的高度不能超过MAX米,不然就会摔死,游戏也会结束。  设计一个程序,计算Jimmy到底地面时可能的最早时间。

思路:

        二维数组,dp[i][j]记录第i个平台,的左或者右到达地面的最早时间。然后怎么找到,你能够跳跃到的状态呢,显然是在该平台的下方,满足该平台的左/右边,在跳跃到的平台的左右界之内,所以我们按照高度排个序,直接O(n)的扫。

#include <iostream>#include <cstdio>#include <algorithm>using namespace std;const int maxn = 1e3 + 5;const int INF = 0x3f3f3f3f;struct node{    int l, r, h;    bool operator <(const node &other)const    {        if(h != other.h)    return h < other.h;        return l <other.l;    }}a[maxn];int dp[maxn][2];int main(){    int T;    scanf("%d", &T);    while(T--)    {        int sx, sy, maxh, n;        scanf("%d%d%d%d", &n , &sx, &sy, &maxh);        for(int i = 0; i < n; i++)        {            int l, r, h;            scanf("%d%d%d", &l, &r, &h);            a[i] = {l, r, h};        }        a[n++] = {sx, sx, sy};        a[n++] = {-INF, INF, 0};        sort(a, a + n);        for(int i = 1; i < n; i++)        {            dp[i][0] = dp[i][1] = INF;            for(int j = i - 1; j >= 0; j--)            {                if(a[i].h - a[j].h > maxh)  continue;                if(a[j].l <= a[i].l && a[j].r >= a[i].l)                {                    dp[i][0] = a[i].h - a[j].h;                    if(j != 0)                        dp[i][0] += min(dp[j][0] + a[i].l-a[j].l, dp[j][1] + a[j].r-a[i].l);                    break;                }            }            for(int j = i - 1; j >= 0; j --)            {                if(a[i].h - a[j].h > maxh)  continue;                if(a[j].l <= a[i].r && a[j].r >= a[i].r)                {                    dp[i][1] = a[i].h - a[j].h;                    if(j != 0)                        dp[i][1] += min(dp[j][0] + a[i].r-a[j].l, dp[j][1] + a[j].r-a[i].r);                    break;                }            }        }        printf("%d\n", min(dp[n - 1][0], dp[n - 1][1]));    }    return 0;}


Longest Ordered Subsequence

思路:

  LIS。


Treats for the Cows

题意:

  给一行n个数,每次可以取出行首或者行末的数,如果第ai是第i次取出的,可以得到ai*i的收益,求最大的总收益。

思路:

        显然是一个经典的区间dp问题。dp[i][j]在第(n-j+i)次,剩余第i个到第j个物品未取的时候的总收益。这样听起来悬乎,其实可以直接考虑,当前剩余第i个到第j个东西。那么你这是第几次取,肯定已知吧,那么一定是剩余第i到第j-1个物品的状态或者剩余第i+1到第j个物品的状态转移来的。

#include <cstdio>#include <iostream>#include <algorithm>using namespace std;const int maxn = 2000 + 5;typedef long long LL;int val[maxn];LL dp[maxn][maxn];int main(){    int n;    scanf("%d", &n);    for(int i = 0; i < n; i++)    {        scanf("%d", &val[i]);    }    for(int i = 0; i < n; i++)    {        dp[i][i] = 1LL * n * val[i];    }    for(int i = n - 1; i >= 0; i--)    {        for(int j = i; j < n; j++)        {            if(j - 1 >= i)                dp[i][j] = max(dp[i][j], dp[i][j - 1] + 1LL * val[j] * (n - j + i));            if(i + 1 <= j)                dp[i][j] = max(dp[i][j], dp[i + 1][j] + 1LL * val[i] * (n - j + i));        }    }    printf("%lld\n", dp[0][n - 1]);    return 0;}

FatMouse and Cheese

题意:

  在n*n的格子上,每个点各有若干块奶酪,胖老鼠从左上角出发,每次最多走k步(只能直走),且下一点必须比这一点的奶酪多,问最多能吃到多少块奶酪。n的范围是100。

思路:

       记忆化搜索。

#include <bits/stdc++.h>using namespace std;const int maxn = 100 + 5;int ma[maxn][maxn];int dp[maxn][maxn];int n, k, ans;int dx[] = {0, 0, -1, 1};int dy[] = {1, -1, 0, 0};#define Inside(x, y) 0 <= x && x < n && 0 <= y && y < nint dfs(int x, int y){    if(dp[x][y])    return dp[x][y];    int ret = 0;//如果是K*K的双重循环会T    for(int kk = 1; kk <= k; kk++)    {        for(int dir = 0; dir < 4; dir++)        {            int xx = dx[dir] * kk, yy = dy[dir] * kk;            if(abs(xx) + abs(yy) > k)   continue;            if(xx == 0 && yy == 0)  continue;            int fx = x + xx, fy = y + yy;            if(Inside(fx, fy) && ma[fx][fy] > ma[x][y])            {                int temp = dfs(fx, fy);                if(temp > ret)  ret = temp;            }        }    }    return dp[x][y] = ret + ma[x][y];}int main(){    while(~scanf("%d%d", &n, &k))    {        if(n == -1 && k == -1)  break;        memset(dp, 0, sizeof(dp));        for(int i = 0; i < n; i++)        {            for(int j = 0; j < n; j++)            {                scanf("%d", &ma[i][j]);            }        }        printf("%d\n", dfs(0, 0));    }    return 0;}


Phalanx

题意:

  给定矩阵,求符合对称矩阵的最大子矩阵的宽度。 这里的对称矩阵是以左下至右上为轴的。矩阵大小1000.

思路:

        直接暴力就行了。

#include <bits/stdc++.h>using namespace std;const int maxn = 1e3 + 5;char ma[maxn][maxn];int dp[maxn][maxn];int main(){    int n;    while(~scanf("%d", &n))    {        if(n == 0)  break;        memset(dp, 0, sizeof(dp));        for(int i = 1; i <= n; i++)        {            scanf("%s", ma[i] + 1);            reverse(ma[i] + 1, ma[i] + 1 + n);        }        int ans = 1;        for(int i = 1; i <= n; i++)        {            for(int j = 1; j <= n; j++)            {                dp[i][j] = 1;                int p1 = i, p2= j;                while(p1 >= 1 && p2 >= 1 && ma[p1][j] == ma[i][p2])                {                    p1--, p2--;                }                int cnt = i - p1;                if(cnt >= dp[i - 1][j - 1] + 1) dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);                else dp[i][j] = max(dp[i][j], cnt);                ans = max(ans, dp[i][j]);            }        }        printf("%d\n", ans);    }    return 0;}


Milking Time

题意:

  奶牛为自己规划下面n时间内的产奶,m个时间段,每个段有a,b,c表示从a时到b时共可产奶c。 挤奶工每次挤奶必须挤完完整的时间段,且每次挤完需要休息r时,求最终可获得的牛奶最大值。

思路:

       简单dp。

#include <cstdio>#include <algorithm>#include <iostream>using namespace std;const int maxn = 1000 + 5;struct node{    int s, e, w;    bool operator <(const node &other)const    {        if(s != other.s)    return s < other.s;        return e < other.e;    }}cow[maxn];int dp[maxn];int main(){    int n, m, R;    scanf("%d%d%d", &n, &m, &R);    for(int i = 1; i <= m; i++)    {        int s, e, w;        scanf("%d%d%d", &s, &e, &w);        cow[i] = {s, e + R, w};    }    sort(cow + 1, cow + 1+ m);    int ans = 0;    for(int i = 1; i <= m; i++)    {        if(cow[i].s > n)    break;        for(int j = 0; j < i; j++)        {            if(cow[j].e <= cow[i].s)            {                dp[i] = max(dp[i], dp[j] + cow[i].w);                ans = max(ans, dp[i]);            }        }    }    printf("%d\n", ans);    return 0;}

Making the Grade

题意:

  就是农夫要修一条路,现在要求这条路要么就是上升的,要么就是下降的,总代价为∑|a[i]-b[i]|,求代价最低的修路方案, (0 ≤ β≤ 1,000,000,000) , (1 ≤ N ≤ 2,000)

思路:

        dp[i][j]:前i个位置,最大元素是原来的路中的第j大的元素的时候,代价最低的修路方案。

#include <cstdio>#include <algorithm>using namespace std;const int maxn = 2e3 + 5;int a[maxn], b[maxn];int dp[maxn][maxn];int solve(int a[], int b[], int n){    for(int i = 1; i <= n; i++)    {        int rec = 0x3f3f3f3f;        for(int j = 1; j <= n; j++)        {            rec = min(rec, dp[i - 1][j]);            dp[i][j] = rec + abs(a[i] - b[j]);        }    }    return *min_element(dp[n] + 1, dp[n] + 1+ n);}int main(){    int n;    scanf("%d", &n);    for(int i = 1; i <= n; i++)    {        scanf("%d", &a[i]);        b[i] = a[i];    }    sort(b + 1, b + 1 + n);    int ans1 = solve(a, b, n);    for(int i = 1; i <= n; i++) b[i] = -b[i];    sort(b + 1, b + 1 + n);    int ans2 = solve(a, b, n);    printf("%d\n", min(ans1, ans2));    return 0;}





阅读全文
0 0