DP专题整理

简单DP

背包问题

《背包九讲》笔记

G - 免费馅饼

HDU - 1176

题意

小明初始站在长度为10的数轴上5的位置。每秒钟会从天上掉下馅饼,给出掉落的位置x和时间T,求最大能接到的馅饼数。

题解

基础DP。
下一秒这个位置最大能接到的馅饼数是上一秒这个位置以及左、右位置馅饼数的最大值,递推即可。

代码

#include <algorithm>
#include <cstring>
using namespace std ;
int a[100001][12];
int f[100001][12];
int main (){
    int n;
    while (scanf("%d", &n) != EOF && n) {
        memset (a, 0, sizeof a) ;
        memset (f, 0, sizeof f) ;
        int x, T, i, j, maxT = 0 , ans = 0 ;
        while (n--) {
            scanf ("%d%d", &x, &T);
            ++a[T][x];
            maxT = max(maxT, T);
        }
        f[1][4] = a[1][4];
        f[1][5] = a[1][5];
        f[1][6] = a[1][6];
        for (i = 2; i <= maxT; ++i){
            for ( j = 0; j < 11; ++j) {
                f[i][j] = f[i-1][j];
                if (j > 0)
                    f[i][j] = max(f[i][j], f[i-1][j-1]);
                if (j < 10)
                    f[i][j] = max(f[i][j], f[i-1][j+1]);
                f[i][j] += a[i][j];
            }
        }
        for (i = 0; i < 11; ++i)
            ans = max(ans, f[maxT][i]);
        printf("%d\n", ans);
    }
    return 0;
}

E - Super Jumping! Jumping! Jumping!

HDU - 1087

题意

给出一个一维棋盘,最左起点最右终点,每个位置都有一个整数值,可以从值小的位置向右跳到值大的位置。得分是路径上值的总和。求最大得分。

题解

基础DP。
下一个点的最大得分是前面所有点中的最大得分点加上它自己的值。

代码

#include <stdio.h>
#include <string.h>

int main(){
    int n, v[1005], dp[1005];
    while (scanf("%d", &n), n) {
        memset(dp, 0, sizeof(dp));
        int maxn = 0;
        for (int i = 1; i <= n; ++i)
            scanf("%d", v+i);
        for (int i = 1; i <= n; ++i){
            int max = 0;
            for (int j = 1; j < i; ++j){
                if (dp[j] > max && v[j] < v[i])
                    max = dp[j];
            }
            dp[i] = max + v[i];
            maxn = maxn<dp[i]? dp[i]: maxn;
        }
        printf("%d\n", maxn);
    }
    return 0;
}

J - FatMouse's Speed

HDU - 1160

题意

给出一群老鼠的体重和奔跑速度,求一个最长的老鼠编号序列,这个序列中的老鼠体重递增,奔跑速度递减。

题解

最长上升(下降)子序列。
先按照体重排个序,然后最长下降子序列DP即可。
WA了好几次,主要是对这个算法还不是很了解,应该是O(nlogn)的时间复杂度。
输出序列用到了前驱数组和递归输出。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;

struct mouse{
    int w, s, id;
}mice[1005];

bool cmp(mouse a, mouse b){
    if (a.w == b.w)
        return a.s > b.s;
    return a.w < b.w;
}

void output(int cur, int pre[]){
    if (cur == 0) return;
    output(pre[cur], pre);
    printf("%d\n", mice[cur].id);
}

int main(){
    int t = 1;
    mice[0].w = 0, mice[0].s = 10005;
    while (~scanf("%d%d", &mice[t].w, &mice[t].s)){
        mice[t].id = t;
        ++t;
    }
    sort(mice+1, mice+t, cmp);
    
    int dp[1005] = {0}, maxn = 1, pre[1005] = {0}, cur = 1;
    for (int i = 1; i < t; ++i){
        for (int j = 0; j < i; ++j){
            if (mice[j].w < mice[i].w && mice[j].s > mice[i].s && dp[j]+1 > dp[i]){
                dp[i] = dp[j]+1;
                pre[i] = j;
                if (dp[i] > maxn){ maxn = dp[i]; cur = i; }
            }
        }
    }
    printf("%d\n", maxn);
    output(cur, pre);
    return 0;
}

L - Common Subsequence

POJ - 1458

题意

给出两个字符串,求它们的最长公共子序列长度。

题解

最长公共子序列,裸题。

if (a[i] == b[j])
    dp[i][j] = dp[i-1][j-1]+1;
else
    dp[i][j] = max(dp[i-1][j], dp[i][j-1]);

代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

int main(){
    char a[1005], b[1005];
    while (~scanf("%s%s", a+1, b+1)){
        int dp[1005][1005] = {0};
        int x = strlen(a+1), y = strlen(b+1);
        for (int i = 1; i <= (int)strlen(a+1); ++i){
            for (int j = 1; j <= (int)strlen(b+1); ++j){
                if (a[i] == b[j])
                    dp[i][j] = dp[i-1][j-1]+1;
                else
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
            }
        }
        printf("%d\n", dp[x][y]);
    }
    return 0;
}

M - Help Jimmy

POJ - 1661

题意

Jimmy从坐标为(x,y)的点掉落下来,速度1单位/s,如果掉落下去的距离超过了max就会摔死。空间中有许多横着的木板,属性为(x1,x2,h)。在木板上可以横向移动,速度1单位/s。求最快到达地面的时间。

题解

基础DP。
dp[i][0]表示从第i块木板左侧掉落的到达地面时间,dp[i][1]表示右侧。转移方程为:

dp[i][0] = 
    p[i].h - p[k].h + 
    min(dp[k][0]+p[i].x1-p[k].x1, 
        dp[k][1]+p[k].x2-p[i].x1);
dp[i][1] = 
    p[i].h - p[k].h + 
    min(dp[k][0]+p[i].x2-p[k].x1, 
        dp[k][1]+p[k].x2-p[i].x2);

代码

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;

struct plat{
    int x1, x2, h;
}p[1005];
int maxn, dp[1005][2];

bool cmp(plat a, plat b){
    return a.h < b.h;
}

void calc_left(int i){
    int k = i - 1;
    while (k > 0 && p[i].h - p[k].h <= maxn){
        if (p[i].x1 >= p[k].x1 && p[i].x1 <= p[k].x2){
            dp[i][0] = p[i].h - p[k].h + min(dp[k][0]+p[i].x1-p[k].x1,
                                             dp[k][1]+p[k].x2-p[i].x1);
            return;
        } else --k;
    }
    if (p[i].h - p[k].h > maxn)
        dp[i][0] = 0x3f3f3f3f;
    else
        dp[i][0] = p[i].h;
}

void calc_right(int i){
    int k = i - 1;
    while (k > 0 && p[i].h - p[k].h <= maxn){
        if (p[i].x2 >= p[k].x1 && p[i].x2 <= p[k].x2){
            dp[i][1] = p[i].h - p[k].h + min(dp[k][0]+p[i].x2-p[k].x1,
                                             dp[k][1]+p[k].x2-p[i].x2);
            return;
        } else --k;
    }
    if (p[i].h - p[k].h > maxn)
        dp[i][1] = 0x3f3f3f3f;
    else
        dp[i][1] = p[i].h;
}

void solve(){
    memset(dp, 0, sizeof dp);
    int n;
    scanf("%d%d%d%d", &n, &p[0].x1, &p[0].h, &maxn);
    ++n, p[0].x2 = p[0].x1;
    for (int i = 1; i < n; ++i)
        scanf("%d%d%d", &p[i].x1, &p[i].x2, &p[i].h);
    p[n].x1 = -20001, p[n].x2 = 20001, p[n].h = 0;
    sort(p, p+n+1, cmp);
    
    for (int i = 1; i <= n; ++i){
        calc_left(i);
        calc_right(i);
    }
    
    printf("%d\n", min(dp[n][0], dp[n][1]));
}

int main(){
    int t;
    scanf("%d", &t);
    while (t--) solve();
    return 0;
}

状态压缩DP

D - Doing Homework

HDU - 1074

题意

给出n门作业的名称、截止时间和所需时间。当超过截止时间时会扣分。求最小的扣分数和做作业的顺序。

题解

状态压缩DP。
看了题解才知道这样做。
首先是二进制法遍历集合全排列,然后遍历当前排列中包含的上一个做完的作业,即遍历每一种作业,找出扣分的最小值作为上一个做完的作业。记录这一状态,并且记录前驱(输出用)。

代码

/******************************
 *File name: hdu1029.cpp
 *Author: wzhzzmzzy
 *Created Time: 日  4/23 20:01:35 2017
 *TODO: HDU 1029 状压DP 水
 ******************************/

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>

using namespace std;

char name[16][200];
int t[1<<15], deadline[16], finish[16];
int pre[1<<15], dp[1<<15];

void output(int m){
    if (!m) return;
    output(m-(1<<pre[m]));
    printf("%s\n", name[pre[m]]);
}

int main(){
    int T;
    scanf("%d", &T);
    while (T--){
        int n;
        scanf("%d", &n);
        int maxn = 1<<n;
        for (int i = 0; i < n; ++i)
            scanf("%s%d%d", name[i], deadline+i, finish+i);
        for (int i = 1; i < maxn; ++i){
            dp[i] = 0x3f3f3f3f;
            for (int j = n-1; j >= 0; --j){
                int te = 1<<j;
                if (!(i&te)) continue;
                int score = t[i-te]-deadline[j]+finish[j];
                if (score < 0) score = 0;
                if (dp[i] > dp[i-te]+score){
                    dp[i] = dp[i-te]+score;
                    t[i] = t[i-te]+finish[j];
                    pre[i] = j;
                }
            }
        }
        printf("%d\n", dp[maxn-1]);
        output(maxn-1);
    }
    return 0;
}

数位DP

不要62

HDU - 2089

题意

求从lr的所有数字中,不含有462的数字有多少。

题解

数位DP入门。
dp[i][j]表示j开头的i位数。首先打表,然后根据读入的数字挨个匹配累加即可。
递推公式:

for (int i = 1; i < 7; ++i)
    for (int j = 0; j < 10; ++j)
        for (int k = 0; k < 10; ++k)
            if (j != 4 && !(j == 6 && k == 2))
                dp[i][j] += dp[i-1][k];

代码

/******************************
 *File name: hdu2089.cpp
 *Author: wzhzzmzzy
 *Created Time: 二  4/25 20:05:44 2017
 *TODO: HDU 2089 数位DP
******************************/

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>

using namespace std;

int dp[8][10];
void init(){
    memset(dp, 0, sizeof dp);
    dp[0][0] = 1;
    for (int i = 1; i < 7; ++i)
        for (int j = 0; j < 10; ++j)
            for (int k = 0; k < 10; ++k)
                if (j != 4 && !(j == 6 && k == 2))
                    dp[i][j] += dp[i-1][k];
}

int calc(int x){
    int digit[8], len = 0, ans = 0;
    while (x > 0){
        digit[++len] = x%10;
        x /= 10;
    }
    digit[len+1] = 0;
    for (int i = len; i; --i){
        for (int j = 0; j < digit[i]; ++j)
            if (j != 4 && !(digit[i+1]==6&&j==2))
                ans += dp[i][j];
        if (digit[i] == 4||(digit[i]==2&&digit[i+1]==6))
            break;
    }
    return ans;
}

int main(){
    int n, m;
    init();
    while (~scanf("%d%d", &n, &m) && n && m){
        printf("%d\n", calc(m+1)-calc(n));
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • KMP 学习记录 kuangbin专题十六——KMP KMP 学习总结 朴素 KMP 算法 拓展 KMP 算法(E...
    染微言阅读 555评论 0 3
  • 简介 搜索迷宫(BFS+队列) 最短路Dijkstra+邻接矩阵Dijkstra+链式前向星+优先队列Bellma...
    染微言阅读 381评论 0 1
  • A - 数字三角形题解:假设getMax(i,j)表示点(i,j)到底部的最长路径,那么getMax(i,j)=m...
    Gitfan阅读 866评论 0 0
  • 开始刷leetcode,简单题就懒得写出来了,把有点难度或者思路有趣的题记录一下。写业务写久了,整个人都变蠢了,需...
    bakaqian阅读 1,451评论 2 1
  • 动态规划(Dynamic Programming) 本文包括: 动态规划定义 状态转移方程 动态规划算法步骤 最长...
    廖少少阅读 3,252评论 0 18