基础dp

Posted icode-xiaohu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基础dp相关的知识,希望对你有一定的参考价值。

  队友的建议,让我去学一学kuangbin的基础dp,在这里小小的整理总结一下吧。

  首先我感觉自己还远远不够称为一个dp选手,一是这些题目还远不够,二是定义状态的经验不足。不过这些题目让我在一定程度上加深了对dp的理解,但要想搞好dp,还需要多多练习啊。

  HDU - 1024 开场高能

  给出一个数列,分成m段,求这m段的和的最大值,dp[i][j]表示遍历到第i个数,已经划分了j段,对于每一个数有两种决策,加入上一个区间段,自己成为一个区间段,故dp[i][j] = max(dp[i-1][j]+a[i],dp[k][j-1]+a[i])     1<=k<=i-1,二维数组肯定不能开,所以使用两个数组,另一个数组保存上一次的状态,这样也可以及时的获得第二种情况的答案,然后再更新一遍旧状态就可以。一开始因为初始化问题错了几次。

  

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
const int N = 1e6+6;
const int INF = 1e9;
#define LL long long
LL dp1[N],dp2[N];
LL a[N];
int main() {
    int n,m;
    while(~scanf("%d%d",&m,&n)) {
        for(int i=1; i <=n; i++) {
            scanf("%lld",&a[i]);
            dp1[i] = dp2[i] = 0;
        }
        dp1[0] = 0;
        dp2[0] = 0;
        for(int i =1; i <= m; i++) {
            LL Max = -INF;
            for(int j=i; j <= n; j++) {
                Max = max(Max,dp2[j-1]);
                dp1[j] = max(dp1[j-1]+a[j],Max+a[j]);
            }
            for(int j =i; j <= n; j++) {
                dp2[j] = dp1[j];
            }
        }
        LL ans = -INF;
        for(int i = m; i <= n; i++) {
            ans = max(ans,dp1[i]);
        }
        printf("%lld\\n",ans);
    }
    return 0;
}
View Code

  HDU - 1029 感觉跟dp没啥关系

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e6+6;
#define LL long long
LL a[N];
int main(){
    int n;
    while(~scanf("%d",&n)){
        for(int i = 0;i < n;i++){
            scanf("%lld",&a[i]);
        }
        sort(a,a+n);
        printf("%lld\\n",a[n/2]);
    }
    return 0;
}
View Code

  HDU - 1069 线性dp,排序后求最大权值

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
struct Rec{
    int c,k,g;
}r[100];
void setRec(int id,int x,int y,int z){
    r[id].c = x;
    r[id].k = y;
    r[id].g = z;
}
int dp[100];
bool ok(int i,int j){
    if(r[i].c<r[j].c && r[i].k<r[j].k) return true;
    if(r[i].c<r[j].k && r[i].k<r[j].c) return true;
    return false;
}
bool cmp(Rec a,Rec b){
    return (a.c*a.k > b.c*b.k);
}
int main()
{
    int n,x,y,z,ca=0;
    while(~scanf("%d",&n)){
        if(!n) break;
        for(int i = 1;i <= 3*n;i += 3){
            scanf("%d%d%d",&x,&y,&z);
            setRec(i,x,y,z);
            setRec(i+1,x,z,y);
            setRec(i+2,y,z,x);
        }
        n = n*3;
        sort(r+1,r+n+1,cmp);
        for(int i=1;i <= n;i++){
//            cout<<r[i].c<<" "<<r[i].k<<" "<<r[i].g<<endl;
            dp[i] = r[i].g;
            for(int j = 1;j < i;j++){
                if(ok(i,j)){
                    dp[i] = max(dp[i],dp[j]+r[i].g);
                }
            }
        }
        int ans = 0;
        for(int i = 1;i <= n;i++){
            ans = max(ans,dp[i]);
        }
        printf("Case %d: maximum height = %d\\n",++ca,ans);
    }
}
View Code

  HDU - 1074  之前就做过,但基础dp里居然有状态压缩,因为这里只是求做作业的顺序,从小到大枚举所有状态,对于每一个状态,添加一个作业递推出新的状态,如果一个状态可以由多个途径达到,选择一个最优的,当所有作业选完以后就是答案,回溯状态输出一下就可以。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 15
#define M (1<<N)+N
const int INF = 1e9;
struct DP {
    int day,red,fa;
} dp[M];
struct Course {
    string name;
    int dl,cost;
} c[N];
void outPut(int k,int n) {
    int f = dp[k].fa;
    if(f == -1) return;
    outPut(f,n);
    for(int i =0; i < n; i++) {
        if((k&(1<<i))!=0 && (f&(1<<i))==0) {
            cout<<c[i].name<<endl;
            break;
        }
    }
}
int main() {
    int T,n,day;
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&n);
        for(int i = 0; i < n; i++) {
            cin>>c[i].name>>c[i].dl>>c[i].cost;
        }
        int up = (1<<n);
        for(int i = 0; i < up; i++) {
            dp[i].fa = -1;
        }
        dp[0].day = dp[0].red = 0;
        for(int i=0; i < up; i++) {
            for(int j=0; j<n; j++) {
                if((i&(1<<j)) == 0) {
                    int k = (i|(1<<j));
                    int nday = dp[i].day+c[j].cost;
                    dp[k].day = nday;
                    int nred = max(0,nday-c[j].dl);
                    if(dp[k].fa == -1) {
                        dp[k].red = dp[i].red+nred;
                        dp[k].fa = i;
                    } else if(dp[k].red > dp[i].red+nred) {
                        dp[k].red = dp[i].red+nred;
                        dp[k].fa = i;
                    }
                }
            }
        }
        printf("%d\\n",dp[up-1].red);
        outPut(up-1,n);
    }
    return 0;
}
View Code

  HDU - 1087 最长递增子序列

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
#define N 1005
const int INF = 1e9;
LL dp[N],a[N];
int main()
{
    int n;
    while(~scanf("%d",&n)){
        if(!n) break;
        LL ans = -INF;
        for(int i = 0;i < n;i++){
            scanf("%lld",&a[i]);
            dp[i] = a[i];
            for(int j = 0;j < i;j++){
                if(a[i]>a[j]) dp[i] = max(dp[i],dp[j]+a[i]);
            }
            ans = max(ans,dp[i]);
        }
        printf("%lld\\n",ans);
    }
    return 0;
}
View Code

  HDU - 1114 完全背包,恰好装满,一开始初始化-1为不可达状态,状态转移的时候判断一下不可达状态就可以

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 10005;
const int M = 505;
int dp[N];
struct Thing{
    int p,w;
}t[M];
int main()
{
    int T,E,F,n;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&E,&F);
        int all = F-E;
        scanf("%d",&n);
        for(int i = 0;i <n;i++){
            scanf("%d %d",&t[i].p,&t[i].w);
        }
        memset(dp,-1,sizeof(dp));
        dp[0] = 0;
        for(int i = 0;i < n;i++){
            for(int j =t[i].w; j<= all;j++){
                int Last = j-t[i].w;
                if(dp[Last] == -1) continue;
                if(dp[j]==-1) dp[j] = dp[Last]+t[i].p;
                else dp[j] = min(dp[j],dp[Last]+t[i].p);
            }
        }
        if(dp[all] == -1){
            printf("This is impossible.\\n");
        }else printf("The minimum amount of money in the piggy-bank is %d.\\n",dp[all]);
    }
    return 0;
}
View Code

  HDU - 1176 数塔,倒着算

  

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 100005
int dp[11][N],a[11][N];
int main()
{
    int n,t,x,Mt;
    while(~scanf("%d",&n)){
        if(!n) break;
        memset(a,0,sizeof(a));
        Mt = 0;
        for(int i=0;i<n;i++){
            scanf("%d%d",&x,&t);
            a[x][t]++;
            Mt = max(Mt,t);
        }
        for(int i=0;i<=10;i++){
            dp[i][Mt+1] = 0;
        }
        for(int i=Mt;i>=0;i--){
            for(int j=0;j <= 10;j++){
                int Max = -1;
                if(j==0) Max = max(dp[j][i+1],dp[j+1][i+1]);
                else if(j==10) Max = max(dp[j][i+1],dp[j-1][i+1]);
                else Max = max(max(dp[j][i+1],dp[j+1][i+1]),dp[j-1][i+1]);
                dp[j][i] = Max + a[j][i];
            }
        }
        printf("%d\\n",dp[5][0]);
    }
    return 0;
}
View Code

  HDU - 1260  基础dp,12点的时候算am,还是pm呢。。题目中都没有这组样例,我还困惑了一会

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 2005
int a[N],b[N],dp[N];
int main() {
    int T,n;
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&n);
        for(int i=1; i<=n; i++) {
            scanf("%d",&a[i]);
        }
        for(int i=2; i<=n; i++) {
            scanf("%d",&b[i]);
        }
        dp[0] = 0;
        for(int i=1; i<=n; i++) {
            if(i==1) dp[i] = a[i];
            else dp[i] = min(dp[i-1]+a[i],dp[i-2]+b[i]);
        }
        int tmp = dp[n];
        int s = tmp%60;
        tmp /= 60;
        int m = tmp%60;
        tmp /= 60;
        int h = tmp%60+8;
        h %= 24;
        bool f = true;
        if(h == 12) f = false;
        if(h > 12) {
            h -= 12;
        }
        printf("%02d:%02d:%02d ",h,m,s);
        if(f) printf("am\\n");
        else printf("pm\\n");
    }
    return 0;
}
View Code

  HDU - 1257  这个题目是有争议的,但是我认为这个题目就是让求,将此序列划分为最长递减序列的最少条数,dp[i]代表第i个子序列的最小值,一个数的决策,当然是加入到距离dp[i]最小的那一个,这样能保证条数最少,而这样做正好就对应了kuangbin的贪心代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e6;
const int INF = 1e9;
int dp[N]; ///dp[i]表示第i个子序列的最小值
int main()
{
    int n,m,tmp;
    while(~scanf("%d",&n)){
        m = 0;
        for(int i=0;i<n;i++){
            scanf("%d",&tmp);
            int Min = INF,End=-1;
            for(int j =0;j < m;j++){
                if(tmp<=dp[j] && dp[j]-tmp < Min){
                    Min = min(Min,dp[j]-tmp);
                    End = j;
                }
            }
            if(End == -1) dp[m++] = tmp;
            else dp[End] = tmp;
        }
        printf("%d\\n",m);
    }
    return 0;
}
View Code

  HDU - 1160

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1005;
const int INF = 1e9;
struct DP{
    int len,pre;
}dp[N];
struct Mice{
    int w,s,id;
}m[N];
bool cmp(Mice a,Mice b){
    if(a.w != b.w) return a.w < b.w;
    else return a.s < b.s;
}
int Stack[N];
int main()
{
//    freopen("in.cpp","r",stdin);
    int tot=0;
    while(scanf("%d %d",&m[tot].w,&m[tot].s) != EOF){
            m[tot].id = tot;
            tot++;
    }
    sort(m,m+tot,cmp);
    for(int i =0;i < tot;i++){
        dp[i].len = 1;
        dp[i].pre = -1;
        for(int j=0;j<i;j++){
            if(m[i].w>m[j].w && m[i].s<m[j].s){
                if(dp[i].len < dp[j].len+1){
                    dp[i].len = dp[j].len+1;
                    dp[i].pre = j;
                }
            }
        }
    }
    int Max = -INF,End=-1;
    for(int i=0;i<tot;i++){
        if(dp[i].len > Max){
            Max = dp[i].len;
            End = i;
        }
    }
    printf("%d\\n",Max);
    int top=0;
    while(End != -1){
        Stack[top++] = m[End].id+1;
        End = dp[End].pre;
    }
    while(top != 0){
        printf("%d\\n",Stack[--top]);
    }
    return 0;
}
View Code

 

  POJ - 1015 这里面我感觉这个题是很难的了,因为我没有找到状态的正确定义,原来是dp[i][k]代表已经选出i个人,差为k的最大和,使用path记录每个状态的决策,在选人的时候通过回溯判断这个人是否已经被选过,我一开始用的vis,各种wa,还没有明白是怎么回事,一开始平移区间还移出了毛病。。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 205;
const int INF = 1e9;
int dp[22][808],path[22][808];
int cha[N],he[N],x[N],y[N],n,m;
///使用path记录此状态选的是哪一个人
bool ok(int i,int j,int k) {
    while(i>0 && path[i][j] != -1) {
        if(path[i][j] == k) return false;
        j -= cha[path[i][j]];
        i--;
    }
    return true;
}
int out[N],os,p,d;
void Print(int i,int j) {
    os = p = d = 0;
    while(i>0 && path[i][j] != -1) {
        int k = path[i][j];
        j -= cha[k];
        i--;
        out[os++] = k;
        p += x[k];
        d += y[k];
    }
}
int main() {
    int ca=0;
    while(~scanf("%d%d",&n,&m)) {
        if(n==0 && m==0) break;
        for(int i = 1; i <= n; i++) {
            scanf("%d%d",&x[i],&y[i]);
            cha[i] = x[i]-y[i];
            he[i] = y[i]+x[i];
        }
        memset(dp,-1,sizeof(dp));
        memset(path,-1,sizeof(path));
        int km = m*20;
        dp[0][km] = 0;
        for(int i=0; i<m; i++) {
            for(int j=0; j<=2*km; j++) {
                if(dp[i][j] == -1) continue;
                for(int k=1; k<=n; k++) {
                    int nj = j+cha[k];
                    if(!ok(i,j,k)) continue;
                    if(dp[i+1][nj]<dp[i][j]+he[k]) {
                        dp[i+1][nj] = dp[i][j] + he[k];
                        path[i+1][nj] = k;
                    }
                }
            }
        }
        int Min = INF,End=-1,Max = -INF;
        for(int i = 0; i <= 2*km; i++) {
            if(dp[m][i]!=-1) {
                if(abs(i-km)<Min) {
                    Min = abs(i-km);
                    End = i;
                    Max = dp[m][i];
                } else POJ3691DNA repair(AC自动机,DP)

[vscode]--HTML代码片段(基础版,reactvuejquery)

HDU4057 Rescue the Rabbit(AC自动机+状压DP)

HDU3247 Resource Archiver(AC自动机+BFS+DP)

[Go] 通过 17 个简短代码片段,切底弄懂 channel 基础

[bzoj1966][Ahoi2005][VIRUS 病毒检测] (字符串dp)