基础DP

Max Sum Plus Plus( HDU - 1024) 题目链接

dp[i][j] = max(dp[i ][j - 1] + a[j],dp[i - 1][k] + a[j]) (i - 1 <= k < j)
dp[i ][j - 1] 代表 与 a[j]是连在前一段的
dp[i - 1][k]代表a[j]自成一段

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
const int INF = 0x3f3f3f3f;
int a[N];
int pre[N];   ///   dp[i - 1][k]  i - 1 < k < j
int dp[N];
int main()
{
    int n,m;
    while(scanf("%d %d",&m,&n) == 2){
        for (int i = 1; i <= n; i++){
            scanf("%d",&a[i]);
        }
        int mx;
        memset(dp,0,sizeof(dp));
        memset(pre,0,sizeof(pre));
        for (int i = 1; i <= m; i++){
            mx = -INF;
            for (int j = i; j <= n; j++){
                dp[j] = max(dp[j - 1],pre[j - 1]) + a[j];
                pre[j - 1] = mx;        /// 下一层的
                mx = max(mx,dp[j]);
            }
        }
        printf("%d\n",mx);
    }
    return 0;
}


Monkey and Banana (HDU - 1069) 题目链接

n种类型的长方体,可叠成的最大高度(底部的长宽都要大于上一层的)


一个长方体可以有三种长宽高
排个序就是基本dp的写法

#include <bits/stdc++.h>
using namespace std;
int dp[111];   //  dp[i]  代表放第i个的最大值
struct node{
    int x,y,z;
    bool operator < (const node& p) const{
        if (x > p.x) return true;
        if (x == p.x && y > p.y) return true;
        return false;
    }
}q[99];
void xyswap(int n)   /// 保证 x > y
{
    for (int i = 1; i <= n; i++){
        if (q[i].x < q[i].y) swap(q[i].x,q[i].y);
    }
}
void add(int i)
{
    q[i + 1].x = q[i].x;
    q[i + 1].y = q[i].z;
    q[i + 1].z = q[i].y;
    q[i + 2].x = q[i].y;
    q[i + 2].y = q[i].z;
    q[i + 2].z = q[i].x;
}
int main()

{
    int n;
    int cnt = 1;
    while (scanf("%d",&n) == 1 && n){
        n *= 3;
        for (int i = 1; i <= n; i += 3){
            scanf("%d %d %d",&q[i].x,&q[i].y,&q[i].z);
            add(i);
        }
        xyswap(n);
        sort(q + 1,q + 1 + n);
     //   for (int i = 1; i <= n; i++){
     //       printf("%d %d %d\n",q[i].x,q[i].y,q[i].z);
     //   }
        memset(dp,0,sizeof(dp));
        for (int i = 1; i <= n; i++){
            dp[i] = q[i].z;
        }
        int ans = q[1].z;
        for (int i = 2; i <= n; i++){
            for (int j = 1; j < i; j++){
                if (q[i].x < q[j].x && q[i].y < q[j].y){
                    if (dp[i] < dp[j] + q[i].z){
                        dp[i] = dp[j] + q[i].z;
                        ans = max(ans,dp[i]);
                    }
                }
            }
        }
        printf("Case %d: maximum height = %d\n",cnt++,ans);
    }
    return 0;
}

Doing Homework (HDU - 1074)题目链接

状压DP
例如转移到 101001 可以 001001、100001、101000三种方式,以此类推
其他的就和普通DP类似

#include <bits/stdc++.h>
using namespace std;
const int INF = 1 << 30;
struct node{
    string name;
    int en,cost;
}s[55];
struct DP{
    int time,score,pre,now;
}dp[1 << 15];
int main()
{
    int t;
    scanf("%d",&t);
    while (t--){
        int n;
        scanf("%d",&n);
        for (int i = 0; i < n; i++){
            cin >> s[i].name >> s[i].en >> s[i].cost;
        }
        int k = 1 << n;
        memset(dp,0,sizeof(dp));
        for (int i = 1; i < k; i++){
            dp[i].score = INF;
            for (int j = n - 1; j >= 0; j--){
                int tem = 1 << j;
                if (i & tem){
                    int past = i - tem;
                    int st = dp[past].time + s[j].cost - s[j].en;
                    if (st < 0) st = 0;
                    if (st + dp[past].score < dp[i].score){
                        dp[i].score = st + dp[past].score;
                        dp[i].now = j;
                        dp[i].pre = past;
                        dp[i].time = dp[past].time + s[j].cost;
                    }
                }
            }
        }
        stack<int> st;
        int tem = k - 1;
        printf("%d\n",dp[tem].score);
        while (tem){
            st.push(dp[tem].now);
            tem = dp[tem].pre;
        }
        while (!st.empty()){
            cout << s[st.top()].name << endl;
            st.pop();
        }
    }
    return 0;
}

Piggy-Bank (HDU - 1114)题目链接

初始重量为e,最大重量为f,输出装满f重量下的最小价值


背包问题

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int v[555],w[555];
int dp[11111];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int e,f,n;
        scanf("%d %d %d",&e,&f,&n);
        f -= e;
        for (int i = 0; i < n; i++){
            scanf("%d %d",&v[i],&w[i]);
        }
        memset(dp,0x3f,sizeof(dp));
        dp[0] = 0;
        for (int i = 0; i < n; i++){
            for (int j = w[i]; j <= f; j++){
                dp[j] = min(dp[j],dp[j - w[i]] + v[i]);
            }
        }
        if (dp[f] == INF) printf("This is impossible.\n");
        else printf("The minimum amount of money in the piggy-bank is %d.\n",dp[f]);
    }
    return 0;
}


FatMouse’s Speed (HDU - 1160)题目链接

老鼠越肥,速度越慢,找到最长
W[m[1]] < W[m[2]] < … < W[m[n]]

and

S[m[1]] > S[m[2]] > … > S[m[n]]


普通dp(就是需要判断两个条件)

#include <bits/stdc++.h>
using namespace std;
const int N = 1111;
struct node{
    int w,v,id;
    bool operator < (const node& p) const{
        if (w == p.w)   return v > p.v;
        return w < p.w;
    }
}s[N];
int pre[N];
int dp[N];
int main()
{
    int cnt = 1;
    while (scanf("%d %d",&s[cnt].w,&s[cnt].v) != EOF && (s[cnt].w || s[cnt].v))  s[cnt].id = cnt,cnt++;
    sort(s + 1, s + cnt);
  //  memset(pre,0,sizeof(pre));
    int ans = 1,index = 1;
    for (int i = 1; i < cnt; i++){
        dp[i] = 1;
        for (int j = 1; j < i; j++){
            if (s[j].w < s[i].w && s[j].v > s[i].v){
                if (dp[j] + 1 > dp[i]){
                    dp[i] = dp[j] + 1;
                    pre[i] = j;
                    if (dp[i] > ans){
                        ans = dp[i];
                        index = i;
                    }
                }
            }
        }
    }
    printf("%d\n",ans);
    stack<int> sa;
    while(pre[index]){
        sa.push(s[index].id);
        index = pre[index];
    }
    printf("%d\n",s[index].id);
    while(!sa.empty()){
        printf("%d\n",sa.top());
        sa.pop();
    }
    return 0;
}


Jury Compromise (POJ - 1015) 题目链接

题意

选出的m 个人,必须满足辩方总分D和控方总分P的差的绝对值|D-P|最小。如果有多种选择方案的|D-P| 值相同,那么选辩控双方总分之和D+P最大的方案即可。

思路

这题可以采取0,1背包的思路,将选出人数看作背包容量,采取价值最小的解,同时规划价值最小的中价值最大的取法

状态转移方程 dp[i + 1][j + sub[k] = dp[i][j] + sum[k];
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
const int N = 22;
const int M = 2 * 20 * 20 + 5;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int sub[M],sum[M];
int dp[N][M];
vector<int> path[N][M];
int main()
{
    int n,m;
    int Case = 1;
    while(scanf("%d %d",&n,&m) == 2 && (n && m)){
        for (int i = 1; i <= n; i++){
            int x,y;
            scanf("%d %d",&x,&y);
            sub[i] = x - y + 20;
            sum[i] = x + y;
        }
        for (int i = 0; i <= m; i++){
            for (int j = 0; j < 1007; j++){
                path[i][j].clear();
            }
        }
        memset(dp,-1,sizeof(dp));
        int mx_size = 20 * m;
        dp[0][0] = 0;

        for (int k = 1; k <= n; k++){
            for (int i = m - 1; i >= 0; i--){
                for (int j = 0; j < 2 * mx_size; j++){
                    if (dp[i][j] >= 0 && dp[i + 1][j + sub[k]] <= dp[i][j] + sum[k]){
                        dp[i + 1][j + sub[k]] = dp[i][j] + sum[k];
                        path[i + 1][j + sub[k]] = path[i][j];
                        path[i + 1][j + sub[k]].push_back(k);
                      //  cout << "sumk = " << sum[k] << "sub= " << sub[k]  << " i+1 = " << i + 1 << " j + sub=" << j + sub[k] << " dp=" << dp[i + 1][j + sub[k]] << endl;
                    }
                }
            }
        }
        int index;
        for (index = 0; dp[m][mx_size - index] == -1 && dp[m][mx_size + index] == -1; index++);
        int sub_sum = dp[m][mx_size - index] > dp[m][mx_size + index] ? -index : index;
        int add_sum = dp[m][mx_size + sub_sum];
      //  cout << "sub_sum = " << sub_sum << " add_sum = " << add_sum << endl;
        int d = (sub_sum + add_sum) >> 1;
        int p = (add_sum - sub_sum) >> 1;
        printf("Jury #%d\nBest jury has value %d for prosecution and value %d for defence:\n",Case++,d,p);
        for (int i = 0; i < m; i++){
            cout << path[m][mx_size + sub_sum][i] << " ";
        }
        cout << endl << endl;
    }
    return 0;
}

Common Subsequence (POJ - 1458)L题链接
找出两个字符串的公共子串最大长度

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 11111;
char x[N],y[N];
int dp[555][555];
int main()
{
    while(scanf("%s %s",x,y) != EOF){
        int len1 = strlen(x);
        int len2 = strlen(y);
     //   cout << x << " " << y << endl;
        memset(dp,0,sizeof(dp));
        for (int i = 0; i < len1; i++){
            for (int j = 0; j < len2; j++){
                if (x[i] == y[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[len1 - 1][len2 - 1]);
    }
    return 0;
}

Help Jimmy (POJ - 1661) 题目链接

场景中包括多个长度和高度各不相同的平台。地面是最低的平台,高度为零,长度无限。

Jimmy老鼠在时刻0从高于所有平台的某处开始下落,它的下落速度始终为1米/秒。当Jimmy落到某个平台上时,游戏者选择让它向左还是向右跑,它跑动的速度也是1米/秒。当Jimmy跑到平台的边缘时,开始继续下落。Jimmy每次下落的高度不能超过MAX米,不然就会摔死,游戏也会结束。

设计一个程序,计算Jimmy到底地面时可能的最早时间。


dp[0][i]表示到达这个平台左边的时间
dp[1][i]表示到达平台右边的时间

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N = 1111;
const int INF = 0x3f3f3f3f;
struct node{
    int l,r,h;
    bool operator < (const node &p) const{
        return h > p.h;
    }
}s[N];
int dp[2][N],vis[N],vis2[N];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n,x,y,mx;
        scanf("%d %d %d %d",&n,&x,&y,&mx);
        for (int i = 1; i <= n; i++){
            scanf("%d %d %d",&s[i].l,&s[i].r,&s[i].h);
        }
        sort(s + 1,s + 1 + n);
        memset(dp,0,sizeof(dp));
        memset(vis,0,sizeof(vis));
        memset(vis2,0,sizeof(vis2));
        s[0].h = y;
        s[0].l = s[0].r = x;
        for (int i = 1; i <= n; i++){
            dp[0][i] = dp[1][i] = INF;
            for (int j = 0; j < i; j++){
                if (s[j].h - s[i].h > mx) continue;
                if (s[i].l <= s[j].l && s[j].l <= s[i].r && !vis[j]){
                    vis[j] = 1;
                    dp[0][i] = min(dp[0][i],dp[0][j] + s[j].l - s[i].l + s[j].h - s[i].h);
                    dp[1][i] = min(dp[1][i],dp[0][j] + s[i].r - s[j].l + s[j].h - s[i].h);
                }
                if (s[i].l <= s[j].r && s[j].r <= s[i].r && !vis2[j]){
                    vis2[j] = 1;
                    dp[0][i] = min(dp[0][i],dp[1][j] + s[j].r - s[i].l + s[j].h - s[i].h);
                    dp[1][i] = min(dp[1][i],dp[1][j] + s[i].r - s[j].r + s[j].h - s[i].h);
                }
            }
          //  printf("i = %d dp0 = %d dp1 = %d\n",i,dp[0][i],dp[1][i]);
        }
        int ans = INF;
        for (int i = 1; i <= n; i++){
            if (s[i].h > mx) continue;
           // printf("i = %d dp0 = %d dp1 = %d sj = %d\n",i,dp[0][i],dp[1][i],s[i].h);
            if (!vis[i]) ans = min(ans,dp[0][i] + s[i].h);
            if (!vis2[i]) ans = min(ans,dp[1][i] + s[i].h);
        }
        if (ans == INF) ans = y;    /// 特判直接落到地面的情况
        printf("%d\n",ans);
    }
    return 0;
}

Longest Ordered Subsequence (POJ - 2533)题目链接

LIS模板题

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N = 1111;
const int INF = 0x3f3f3f3f;
int a[N],b[N];
void solve(int &n)
{
    int len = 0;
    b[0] = -INF;
    for (int i = 1; i <= n; i++){
        if (a[i] > b[len]){
            b[++len] = a[i];
        }
        else {
            int l = 0,r = len;
            while(l <= r){
                int mid = (l + r) >> 1;
                if (a[i] > b[mid]) l = mid + 1;
                else r = mid - 1;
            }
            b[l] = a[i];
        }
    }
    printf("%d\n",len);
}
int main()
{
    int n;
    while(scanf("%d",&n) == 1){
        for (int i = 1; i <= n; i++){
            scanf("%d",&a[i]);
        }
        solve(n);
    }
    return 0;
}


Treats for the Cows (POJ - 3186)题目链接

给n个数,排成一排,每次可以从队头或者队尾拿一个数,权值为第几个拿的*这个数,求最大和


从区间为一个长度往区间[1,n]进行dp
dp[i][j] 表示还剩下[i,j]这个区间可以取得的权值
dp[i][j] = max(dp[i + 1][j] + cnt * a[i],dp[i][j - 1] + cnt * a[j]) /// cnt 代表第几次拿

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N = 2222;
const int INF = 0x3f3f3f3f;
int a[N];
int dp[N][N];
int main()
{
    int n;
    while(scanf("%d",&n) == 1){
        memset(dp,0,sizeof(dp));
        for (int i = 1; i <= n; i++){
            scanf("%d",&a[i]);
            dp[i][i] = a[i] * n;
          //  printf("dpii = %d\n",dp[i][i]);
        }
        for (int i = 1; i < n; i++){
            for (int j = 1; j + i <= n; j++){
                int len = n - i;
                dp[j][j + i] = max(dp[j + 1][j + i] + len * a[j],dp[j][j + i - 1] + len * a[j + i]);
            //    printf("j = %d j + i = %d dp = %d\n",j,j + i,dp[j][j + i]);
            }
        }
        printf("%d\n",dp[1][n]);
    }
    return 0;
}

FatMouse and Cheese (HDU - 1078)题目链接


dfs和剪枝还是没学到家

#include <bits/stdc++.h>
using namespace std;
const int N = 111;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int n,k;
int a[N][N];
int dp[N][N];
int to[4][2] = {0,1,0,-1,1,0,-1,0};
int dfs(int x,int y)
{
    if(dp[x][y]) return dp[x][y];
    int ans = 0;
    for (int j = 1; j <= k; j++){
        for (int i = 0; i < 4; i++){
            int x1 = x + j * to[i][0];
            int y1 = y + j * to[i][1];
            if (x1 < 0 || x1 >= n || y1 < 0 || y1 >= n) continue;
            if (a[x1][y1] <= a[x][y]) continue;
            ans = max(ans,dfs(x1,y1));
        }
    }
    return dp[x][y] = ans + a[x][y];
}
int main()
{
    while(scanf("%d %d",&n,&k) == 2){
        if (n == -1 && k == -1) break;
        for (int i = 0; i < n; i++){
            for (int j = 0; j < n; j++){
                scanf("%d",&a[i][j]);
            }
        }
        memset(dp,0,sizeof(dp));
        int ans = dfs(0,0);
        cout << ans << endl;
    }
    return 0;
}

Dollar Dayz (POJ - 3181) 题目链接

1-k种权值,能够有多少种凑成n的方式

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1111;
const int INF = 0x3f3f3f3f;
typedef long long ll;
const ll mod = 1e18;
ll dp[N][N];
ll dp2[N][N];
int main()
{
    int n,k;
    while(scanf("%d %d",&n,&k) == 2){
        memset(dp,0,sizeof(dp));
        memset(dp2,0,sizeof(dp2));
        for (int i = 0; i <= k; i++){
            dp2[i][0] = 1;
        }
        for (int i = 1; i <= k; i++){
            for (int j = 1; j <= n; j++){
              //  dp[i][j] += dp[i - 1][j];
               // if (j > i) dp[i][j] += dp[i][j - i];
                if (j >= i){
                    dp[i][j] = dp[i - 1][j] + dp[i][j - i] + (dp2[i - 1][j] + dp2[i][j - i]) / mod;
                    dp2[i][j] = (dp2[i - 1][j] + dp2[i][j - i]) % mod;
                }
                else {
                    dp[i][j] = dp[i - 1][j];
                    dp2[i][j] = dp2[i - 1][j];
                }
            }
        }
        if (dp[k][n]) cout << dp[k][n];
        cout << dp2[k][n] << endl;
    }
    return 0;
}

Milking Time (POJ - 3616)题目链接

01背包问题,就是把重量改成了区间

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5;
const int M = 1111;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int dp[N];
struct node{
    int s,e,v;
    bool operator < (const node &p) const{
        if (s == p.s) return e < p.e;      /// s,e排序的顺序可以交换,不影响结果
        return s < p.s;
    }
}q[M];
int main()
{
    int n,m,r;
    while(scanf("%d %d %d",&n,&m,&r) == 3){
        int mx = -1;
        memset(dp,0,sizeof(dp));
        for (int i = 0;i < m;i++){
            scanf("%d %d %d",&q[i].s,&q[i].e,&q[i].v);
            mx = max(mx,q[i].e);
        }
        mx = min(mx,n);
        sort(q, q + m);
        for (int i = 0; i < m; i++){
            for (int j = mx; j >= q[i].e; j--){
                int t = q[i].s - r;
                if (t < 0) t = 0;
                dp[j] = max(dp[j],dp[t] + q[i].v);
            }
        }
        cout << dp[mx] << endl;
    }
    return 0;
}

Phalanx HDU - 2859题目链接

在这里插入图片描述
最大对称子图

#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 7;
const int INF = 0x3f3f3f3f;
typedef long long ll;
char mat[N][N];
int dp[N][N];
int main()
{
    int n;
    while(scanf("%d",&n) == 1 && n){
        memset(mat,0,sizeof(mat));
        memset(dp,0,sizeof(dp));
        for (int i = 1; i <= n; i++){
            for (int j = 1; j <= n; j++){
                scanf(" %c",&mat[i][j]);
            }
        }
        int ans = 0;
        for (int i = 1; i <= n; i++){
            for (int j = 1; j <= n; j++){
                int x = i;
                int y = j;
                int cnt = 0;
                while(mat[x][j] == mat[i][y]){
                    x--;
                    y++;
                    cnt++;
                }
                if (cnt > dp[i - 1][j + 1]) dp[i][j] = dp[i - 1][j + 1] + 1;
                else dp[i][j] = cnt;
                ans = max(ans,dp[i][j]);
            }
        }
        cout << ans << endl;
    }
    return 0;
}

Making the Grade POJ - 3666 题目链接

给出长度为n的整数数列,每次可以将一个数加1或者减1,最少要多少次可以将其变成单调增或者单调减(不严格).
结果序列肯定和原序列的数字相同(不会证明)
dp[i][j] 表示 前i个数,并且第i个数变成第j个大的数的费用
dp[i][j] = min(dp[i-1][1…j]) + abs(a[i] - w[j]);

题目有点问题,只要求不递减即可
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 2e3 + 5;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int a[N],w[N];
ll dp[N][N];
int main()
{
    int n;
    while(scanf("%d",&n) == 1 && n){
        for (int i = 1; i <= n; i++){
            scanf("%d",&a[i]);
            w[i] = a[i];
        }
        sort(w + 1,w + 1 + n);
        for (int i = 1; i <= n; i++){
            ll mi = INF;
            for (int j = 1; j <= n; j++){
                mi = min(mi,dp[i - 1][j]);
                dp[i][j] = abs(a[i] - w[j]) + mi;
            }
        }
        ll ans = INF;
        for (int i = 1; i <= n; i++){
            ans = min(ans,dp[n][i]);
        }
        cout << ans << endl;
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值