基础dp

1、

聪明的kk

时间限制:1000 ms  |  内存限制:65535 KB
难度:3
 
描述
聪明的“KK”
非洲某国展馆的设计灵感源于富有传奇色彩的沙漠中陡然起伏的沙丘,体现出本国不断变换和绚丽多彩的自然风光与城市风貌。展馆由五部分组成,馆内影院播放名为《一眨眼的瞬间》的宽银幕短片,反映了建国以来人民生活水平和城市居住环境的惊人巨变。
可移动“沙丘”变戏法 的灵感源于其独特而雄伟的自然景观——富于传奇色彩的险峻沙丘。宏伟的结构、可循环的建材,与大自然相得益彰。环绕一周,发现它正是从沙丘那不断变换的形态中汲取灵感的。外形逼真到无论从哪个角度去观察,都能清楚地辨识出沙丘的特征。
它“坡面”高达20米,微风吹来,你是否感觉到沙的流动?用手去触碰,却发现原来是“魔术戏法”。它表面的不锈钢面板呈现出一种富于变幻的色彩,从不同角度观察,呈现不同色泽,由此来模仿流动沙丘的光感。
走进第三展厅有一个超大的屏幕,通过奇妙的特效,让观众犹如亲身来到浩瀚的沙漠。更为奇妙的是,只见一个小动物“KK”正从沙漠区域(矩形)的左上角沿着向右或向下的方向往右下角跑去。KK太聪明了,它居然能在跑的过程中会选择吃掉尽可能多的虫子线路。
你知道它吃掉多少虫子吗?
 
输入
第一行:N M (1≤N M≤20 0≤Xij≤500(i=1,2„.N, j=1,2„,M)
)表示沙漠是一个N*M的矩形区域
接下来有N行:每行有M个正整数,Xi1 Xi2 ……Xim 表示各位置中的虫子数(单个空格隔开)
假设“KK”只能向右走或向下走。
输出
输出有一个整数, 表示“KK”吃掉最多的虫子数。
样例输入
3 4
3 1 2 8
5 3 4 6
1 0 2 3
样例输出
24
来源
第三届河南省程序设计大赛
上传者
苗栋栋

 找当前子问题最优解。

#include <cstdio>
#include <cstring>
#define max(a,b) (a>b?a:b)
const int N = 21;
int dp[N][N], num[N][N]; 
int main(){
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF){
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                scanf("%d", &num[i][j]);
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                dp[i][j]=max(dp[i-1][j], dp[i][j-1])+num[i][j];
        printf("%d
", dp[n][m]);
    }
    return 0;
}

2、

最大连续子串和
dp[n]表示当前1->n最大子串和 然后再找最大值就行了, 也算dp思想吧。
得到状态转移方程:

  dp[i]=dp[i-1]>0? dp[i]+dp[i-1]+num[i]:dp[i]+num[i];

#include <cstdio>
#include <cstring>
const int N = 21;
int num[N], dp[N];
#define max(a, b) (a>b?a:b)
int main(){
    int n;
    while(scanf("%d", &n) != EOF){
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++)
            scanf("%d", &num[i]);
        int maxsum = -1;
        for(int i = 1; i <= n; i++){    
            dp[i]=dp[i-1]>0?dp[i]+dp[i-1]+num[i]:dp[i]+num[i];
            maxsum=max(maxsum, dp[i]);
        }
        printf("%d
", maxsum);  
    }
    return 0;
} 

hdoj 1003  确定首尾位置++

#include <cstdio>
#include <cstring> 
#define max(a, b) (a)>(b)?(a):(b)  
int num[100001], dp[100001];
int main()
{
    int t; 
    scanf("%d", &t);
    int Q=1;
    while(t--)
    {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
            scanf("%d", &num[i]);
        memset(dp, 0, sizeof(dp));
        int maxnum = -100000000;
        int s=1, t=1, f=1, d=1;
        for(int i = 1; i <= n; i++)
        {
            if(dp[i-1]<0)
            {s=i; t=i;}
            dp[i]=dp[i-1]>0? dp[i]+dp[i-1]+num[i]:dp[i]+num[i];
            t++;
            if(maxnum<dp[i])
            {f=s; d=t-1;maxnum=dp[i];}
        }
        if(Q != 1)
            printf("
");
        printf("Case %d:
%d %d %d
", Q++, maxnum, s, t-1);
    }
    return 0;
}

3、

LCS

看代码应该能看出做法。

两层循环走向是右下方 即:(1, 1)--> (n, n). dp[i][j]=max(dp[i-1][j], dp[i][j-1]); dp[i][j]==0;  所以通过dp[i-1][j-1]来更新dp[i][j]; dp[i][j]存储的是dp[i][j]处相同字符最多个数; i, j 1 开始, 通过dp[i-1][j-1]逐步更新dp[i][j];

#include <cstdio>
#include <cstring>
#define max(a, b) (a>b?a:b)
const int N = 15;
const int M = 16;
char a[N], b[M], dp[N][M];
int main(){
    while(scanf("%s%s", a, b) != EOF){
        int lena = strlen(a);
        int lenb = strlen(b);
        for(int i = 1; i <= lena; i++)
            for(int j = 1; j <= lenb; j++){
                if(a[i-1] == b[j-1])
                    dp[i][j]=dp[i-1][j-1]+1;
                else
                    dp[i][j]=max(dp[i-1][j], dp[i][j-1]);
            }
        printf("%d
", dp[lena][lenb]);
    }
    return 0;
} 

4、

LIS:

poj 3903 LIS;

给定数组中选取一个序列满足:a rising trend is a subsequence pi1 < pi2 < ... < pik, with i1 < i2 < ... < ik.

#include <iostream>
const int N = 100000+100;

using namespace std;

int a[N], f[N], d[N];   //d[N]用于记录a[0......i]的最大长度; 

int bsearch(int f[], int size, int a)
{
    int l=0, r= size-1;
    while(l <= r)
    {
        int mid=(l+r)>>1;
        if(a >f[mid-1] && a<= f[mid]) return mid; // a >=f[mid] && a< f[mid];
        else if(a <f[mid]) r= mid-1;
        else l= mid+1;
    }
}
int LIS(int a[], int n)
{
    int j, size= 1;
    f[0]= a[0]; d[0]= 1;
    for(int i=1; i< n; i++)
    {
        if(a[i] <= f[0]) j= 0;               // < 
        else if(a[i] >f[size-1]) j= size++;  // >= 
        else j =bsearch(f, size, a[i]);
        f[j]= a[i]; d[i]= j+1;
    }
    return size;
}
int main()
{
    int n; 
    while(scanf("%d", &n) != EOF)
    {
        for(int i=0; i< n; i++) 
            scanf("%d", &a[i]);
        LIS(a, n); //int maxL= LIS(a, n); 
        int maxL= 0;
        for(int i=0; i< n; i++)
            maxL=max(maxL, d[i]);
        printf("%d
", maxL);
    }
    return 0;    
}

5、

Dfs + DP思想

skiing

时间限制:3000 ms  |  内存限制:65535 KB
难度:5
 
描述
Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长底滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子 
1 2 3 4 5

16 17 18 19 6

15 24 25 20 7

14 23 22 21 8

13 12 11 10 9

一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-...-3-2-1更长。事实上,这是最长的一条。
 
输入
第一行表示有几组测试数据,输入的第二行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。
后面是下一组数据;
输出
输出最长区域的长度。
样例输入
1
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
样例输出
25
来源
经典题目
上传者
iphxer
#include <cstdio>
#define N 101
#define max(a, b) ((a)>(b)?(a):(b))
int ac[4][2]={0, 1, 0, -1, -1, 0, 1, 0};
int num[N][N], dp[N][N];
int maxlen;
int n, m;
void Dfs(int x, int y){
    maxlen=max(maxlen, dp[x][y]);
    for(int i = 0; i < 4; i++){
        int xx = x + ac[i][0];
        int yy = y + ac[i][1];
        if(xx>=0 && xx<n && yy>=0 && yy<m && num[xx][yy] > num[x][y]){
            dp[xx][yy]=max(dp[xx][yy], dp[x][y]+1);
            Dfs(xx, yy);
        }
    }    
}
int main(){
    int t;
    scanf("%d", &t);
    while(t--){
        maxlen = 1;
        scanf("%d%d", &n, &m);
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++){
                scanf("%d", &num[i][j]);
                dp[i][j] = 1;
            }
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
                Dfs(i, j);
        printf("%d
", maxlen);
    }
    return 0;
} 

6、01背包

二维数组中01背包, dp[n][m]代表前n个物品在容量为m的背包中所能容纳最大值。 当前状态与前在m容量背包中前n-1个物品中最大价值 dp[n-1][v]、 以及防入当前物品m后价值dp[n-1][v-vol[i]]相关. 取最优解得方程。

dp[i][v]=max(dp[i-1][v], dp[i-1][v-vol[i]]+val[i]); 方向  v: 0--->v;  n: 1-->n;

一维:

dp[v]=max(dp[v], dp[v-vol[i]]+val[i]);  v: v--->vol[i];  n: 1-->n

Poj3624  http://poj.org/problem?id=3624

#include <cstdio>
#include <cstring>
#define N 3403 
#define M 12881
#define max(a, b) ((a)>(b)?(a):(b))
int w[N], val[N], dp[M];
int main(){
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF){
        memset(dp, 0, sizeof(dp)); 
        for(int i = 1; i <= n; i++)
            scanf("%d%d", &w[i], &val[i]);
        for(int i = 1; i <= n; i++)
            for(int v = m; v >= w[i]; v--)
                dp[v]=max(dp[v], dp[v-w[i]]+val[i]);
        printf("%d
", dp[m]);
    }
    return 0;
} 
#define M = ???
int dp[M][M];
int main(){
    for(int i = 0; i < m; i++)
        for(int v = 0; v < n; v++){
            if(v < vol[i])
                dp[i][v]=dp[i-1][v]; 
            else    
                dp[i][v]=max(dp[i-1][v], dp[i-1][v-vol[i]]+val[i]);
        }
} 

7、完全背包

完全背包每种物品有无限个, 状态方程dp[v] = max(dp[v], dp[v-volume[i]]+value[i]);   第二层循环 v方向   0---->v  代表当前状态。

dp[i][v]=max(dp[i-1][v], dp[i][v-volume[i]]+value[i]) == right? 

hdoj 1114--Piggy-Bank

三个数, 分别代表空袋, 装满后袋重, 和钱的类型数(多重背包)。  求最小恰好可以装满袋子的钱数。

求最少放多少。
#include <cstdio>
#define N 501
#define M 10001 
#define min(a, b) ((a)<(b)?(a):(b))
const int MAXN = 5000001; 
int volume[N], value[N], dp[M]; 
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", &value[i], &volume[i]);
        for(int i = 0; i <= f; i++)
            dp[i] = MAXN;
        dp[0] = 0;  
        for(int i = 0; i < n; i++)
            for(int v = volume[i]; v <= f; v++)
                dp[v] = min(dp[v], dp[v-volume[i]]+value[i]);  
        if(dp[f]==MAXN)
            printf("This is impossible.
");
        else
            printf("The minimum amount of money in the piggy-bank is %d.
", dp[f]);
            
    }
    return 0;
}
View Code
#include <cstdio>
#define N 501 
#define M 10001
#define min(a, b) ((a)<(b)?(a):(b))
const int MAXN = 5000001; 
int volume[N], value[N], dp[N][M]; 
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 = 1; i <= n; i++)
            scanf("%d%d", &value[i], &volume[i]);
        for(int i = 0; i <= n; i++)
            for(int j = 0; j <= f; j++){
                if(j==0)
                    dp[i][0]=0;
                else
                    dp[i][j]=MAXN;
            }
        for(int i = 1; i <= n; i++)
            for(int v = 0; v <= f; v++)
                if(v < volume[i])
                    dp[i][v] = dp[i-1][v];
                else 
                    dp[i][v]=min(dp[i-1][v], dp[i][v-volume[i]]+value[i]);
        if(dp[n][f]==MAXN)
            printf("This is impossible.
");
        else
            printf("The minimum amount of money in the piggy-bank is %d.
", dp[n][f]);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/soTired/p/5081877.html