多维动态规划 (共六题)

来源:互联网 发布:淘宝网的域名怎么设置 编辑:程序博客网 时间:2024/05/22 06:33

这六道题难度依次递升

P1508 Likecloud-吃、吃、吃

URL: https://www.luogu.org/problem/show?pid=1508#sub
题目背景

问世间,青春期为何物?

答曰:“甲亢,甲亢,再甲亢;挨饿,挨饿,再挨饿!”

题目描述

正处在某一特定时期之中的李大水牛由于消化系统比较发达,最近一直处在饥饿的状态中。某日上课,正当他饿得头昏眼花之时,眼前突然闪现出了一个n*m(n and m<=200)的矩型的巨型大餐桌,而自己正处在这个大餐桌的一侧的中点下边。餐桌被划分为了n*m个小方格,每一个方格中都有一个圆形的巨型大餐盘,上面盛满了令李大水牛朝思暮想的食物。李大水牛已将餐桌上所有的食物按其所能提供的能量打了分(有些是负的,因为吃了要拉肚子),他决定从自己所处的位置吃到餐桌的另一侧,但他吃东西有一个习惯——只吃自己前方或左前方或右前方的盘中的食物。

由于李大水牛已饿得不想动脑了,而他又想获得最大的能量,因此,他将这个问题交给了你。

每组数据的出发点都是最后一行的中间位置的下方!

输入输出格式

输入格式:
[输入数据:]

第一行为m n.(n为奇数),李大水牛一开始在最后一行的中间的下方

接下来为m*n的数字距阵.

共有m行,每行n个数字.数字间用空格隔开.代表该格子上的盘中的食物所能提供的能量.

数字全是整数.

输出格式:
[输出数据:]

一个数,为你所找出的最大能量值.

输入输出样例

输入样例#1:
6 7
16 4 3 12 6 0 3
4 -5 6 7 0 0 2
6 0 -1 -2 3 6 8
5 3 4 0 0 -2 7
-1 7 4 0 7 -5 6
0 -1 3 4 12 4 2
输出样例#1:
41
说明

快吃!快吃!快吃!

水题

#include<stdio.h>const int MAXN=2e2+5;int arr[MAXN][MAXN],dp[2][MAXN],n,m;int max(int a,int b,int c){    int t=a>b?a:b;    return t>c?t:c;}int main(){    scanf("%d%d",&m,&n);    for(int i=0;i<m;++i){        for(int j=1;j<=n;++j){            scanf("%lld",&arr[i][j]);        }    }    for(int i=0;i<=n+1;++i) dp[0][i]=dp[1][i]=-1e9;    dp[m&1][n/2+1]=0;    int ans=0;    for(int i=m-1;i>=0;--i){        for(int j=1;j<=n;++j){            dp[i&1][j]=max(dp[~i&1][j],dp[~i&1][j-1],dp[~i&1][j+1])+arr[i][j];        }    }    for(int i=1;i<=n;++i) if(dp[0][i]>ans) ans=dp[0][i];    printf("%lld\n",ans);    return 0;}

P1387 最大正方形

URL: https://www.luogu.org/problem/show?pid=1387#sub

题目描述

在一个n*m的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长。

输入输出格式

输入格式:
输入文件第一行为两个整数n,m(1<=n,m<=100),接下来n行,每行m个数字,用空格隔开,0或1.

输出格式:
一个整数,最大正方形的边长

输入输出样例

输入样例#1:
4 4
0 1 1 1
1 1 1 0
0 1 1 0
1 1 0 1
输出样例#1:
2

xjb暴力即可

#include<stdio.h>#include<algorithm>using namespace std;const int MAXN=1e2+2;int arr[MAXN][MAXN],sum[MAXN][MAXN],n,m; int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;++i){        for(int j=1;j<=m;++j){            scanf("%d",&arr[i][j]);            sum[i][j]=sum[i][j-1]+arr[i][j];        }    }    int a=0;    for(int i=1;i<=n;++i){        for(int j=1;j<=n;++j){            int k=j;            while(arr[i][k]) ++k;            --k;            int d=k-i+1;            for(int u=0;u+i<=n&&!(k<j||k-j<u);++u){                while(k>=j&&sum[u+i][k]-sum[u+i][j-1]!=k-j+1) --k;            //  printf("[%d %d]  %d ---> %d\n",i,j,u,k);                a=max(a,min(u+1,k+1-j));            }        }    }    printf("%d\n",a);    return 0;}

P1855 榨取kkksc03

URL: https://www.luogu.org/problem/show?pid=1855#sub

题目描述

Kkksc03的时间和金钱是有限的,所以他很难满足所有同学的愿望。所以他想知道在自己的能力范围内,最多可以完成多少同学的愿望?

输入输出格式

输入格式:
第一行,n M T,表示一共有n(n<=100)个愿望,kkksc03 的手上还剩M(M<=200)元,他的暑假有T(T<=200)分钟时间。

第2~n+1行 mi,ti 表示第i个愿望所需要的时间和金钱。

输出格式:
一行,一个数,表示kkksc03最多可以实现愿望的个数。

输入输出样例

输入样例#1:
6 10 10
1 1
2 3
3 2
2 5
5 2
4 3
输出样例#1:
4
说明

提示 第1,2,3,6个

#include<stdio.h>const int MAXN=202;int dp[MAXN][MAXN],n,M,T;int max(int a,int b){return a>b?a:b;}int main(){    scanf("%d%d%d",&n,&M,&T);    for(int i=0;i<n;++i){        int w,v;        scanf("%d%d",&w,&v);        for(int t=T;t>=w;--t){            for(int m=M;m>=v;--m){                dp[t][m]=max(dp[t][m],dp[t-w][m-v]+1);            }        }    }    int ans=0;    for(int i=0;i<=T;++i) for(int j=0;j<=M;++j) ans=max(ans,dp[i][j]);    printf("%d\n",ans);    return 0;}

P1736 创意吃鱼法

URL: https://www.luogu.org/problem/show?pid=1736#sub

题目描述

回到家中的猫猫把三桶鱼全部转移到了她那长方形大池子中,然后开始思考:到底要以何种方法吃鱼呢(猫猫就是这么可爱,吃鱼也要想好吃法 ^_*)。她发现,把大池子视为01矩阵(0表示对应位置无鱼,1表示对应位置有鱼)有助于决定吃鱼策略。

在代表池子的01矩阵中,有很多的正方形子矩阵,如果某个正方形子矩阵的某条对角线上都有鱼,且此正方形子矩阵的其他地方无鱼,猫猫就可以从这个正方形子矩阵“对角线的一端”下口,只一吸,就能把对角线上的那一队鲜鱼吸入口中。

猫猫是个贪婪的家伙,所以她想一口吃掉尽量多的鱼。请你帮猫猫计算一下,她一口下去,最多可以吃掉多少条鱼?

输入输出格式

输入格式:
有多组输入数据,每组数据:

第一行有两个整数n和m(n,m≥1),描述池塘规模。接下来的n行,每行有m个数字(非“0”即“1”)。每两个数字之间用空格隔开。

对于30%的数据,有n,m≤100

对于60%的数据,有n,m≤1000

对于100%的数据,有n,m≤2500

输出格式:
只有一个整数——猫猫一口下去可以吃掉的鱼的数量,占一行,行末有回车。

输入输出样例

输入样例#1:
4 6
0 1 0 1 0 0
0 0 1 0 1 0
1 1 0 0 0 1
0 1 1 0 1 0
输出样例#1:
3
说明

右上角的

1 0 0 0 1 0 0 0 1

题解

这是一道比较麻烦的题,我压缩代码了,不然可能会长很多。复杂度 o(n2)

o(n4) 的暴力应该谁都能想出来

我们可以记录斜着的1连续长度o(n2),然后在判断是否可以

由于判断的逻辑可以优化到o(1),那么整体复杂度就是o(n2)

#include<stdio.h>const int MAXN=2505;int dp[2][2][MAXN],zore[MAXN],arr[MAXN],n,m;int max(int a,int b){return a>b?a:b;}int min(int a,int b,int c){    int t=a<b?a:b;    return t<c?t:c;}int main(){    scanf("%d%d",&n,&m);    int ans=0;    for(int i=1;i<=n;++i){        for(int j=1,l=0;j<=m;++j){            scanf("%d",&arr[j]);            int t=arr[j];            dp[i&1][0][j]=t*(dp[~i&1][0][j-1]+1);            dp[i&1][0][j]=min(dp[i&1][0][j],zore[j]+1,l+1);            l=(1-t)*(l+1);            ans=max(ans,dp[i&1][0][j]);        }        for(int j=m,l=0;j>0;--j){            int t=arr[j];            dp[i&1][1][j]=t*(dp[~i&1][1][j+1]+1);            dp[i&1][1][j]=min(dp[i&1][1][j],zore[j]+1,l+1);            l=(1-t)*(l+1);            ans=max(ans,dp[i&1][1][j]);            zore[j]=(1-t)*(zore[j]+1);        }    }    printf("%d\n",ans);    return 0;}

P1006 传纸条

URL :https://www.luogu.org/problem/show?pid=1006
题目描述

小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n)。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。

在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。

还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用0表示),可以用一个0-100的自然数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度只和最大。现在,请你帮助小渊和小轩找到这样的两条路径。

输入输出格式

输入格式:
输入文件message.in的第一行有2个用空格隔开的整数m和n,表示班里有m行n列(1<=m,n<=50)。

接下来的m行是一个m*n的矩阵,矩阵中第i行j列的整数表示坐在第i行j列的学生的好心程度。每行的n个整数之间用空格隔开。

输出格式:
输出文件message.out共一行,包含一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。

输入输出样例

输入样例#1:
3 3
0 3 9
2 8 5
5 7 0
输出样例#1:
34
说明

【限制】

30%的数据满足:1<=m,n<=10

100%的数据满足:1<=m,n<=50

NOIP 2008提高组第三题

题解

以起点出发到终点同时走,dp[x1][y1][x2][y2] 表示走到(x1,y1) ,(x2,y2) 时的值

由于 x1+y1=x2+y2=Step 所以dp可以压缩一维用dp[setp][x1][x2] 表示

转移方程就是从能走到 (x1,y1) ,(x2,y2) 的方案中选取最优的

#include<stdio.h>const int MAXN=105;int dp[MAXN<<1][MAXN][MAXN],arr[MAXN][MAXN],n,m;int max(int a,int b,int c,int d){    a=a>b?a:b;    a=a>c?a:c;    return a>d?a:d;}int main(){    scanf("%d%d",&m,&n);    for(int i=1;i<=m;++i){        for(int j=1;j<=n;++j){            scanf("%d",&arr[i][j]);        }    }    dp[1][1][1]=arr[1][1];    for(int step=1;step<=n+m;++step){        for(int x1=1;x1<=n;++x1)            for(int x2=1;x2<=n;++x2){                if(x1==x2) continue;                dp[step][x1][x2]=arr[step-x1][x1]+arr[step-x2][x2]+                    max(dp[step-1][x1][x2],                    dp[step-1][x1-1][x2],                    dp[step-1][x1][x2-1],                    dp[step-1][x1-1][x2-1]);            }    }    int ans=max(dp[n+m][n][n],dp[n+m][n-1][n],dp[n+m][n][n-1],dp[n+m][n-1][n-1])+arr[m][n];    printf("%d\n",ans);    return 0;}

P1417 烹调方案

URL: https://www.luogu.org/problem/show?pid=1417#sub

题目背景

由于你的帮助,火星只遭受了最小的损失。但gw懒得重建家园了,就造了一艘飞船飞向遥远的earth星。不过飞船飞到一半,gw发现了一个很严重的问题:肚子饿了~

gw还是会做饭的,于是拿出了储藏的食物准备填饱肚子。gw希望能在T时间内做出最美味的食物,但是这些食物美味程度的计算方式比较奇葩,于是绝望的gw只好求助于你了。

题目描述

一共有n件食材,每件食材有三个属性,ai,bi和ci,如果在t时刻完成第i样食材则得到ai-t*bi的美味指数,用第i件食材做饭要花去ci的时间。

众所周知,gw的厨艺不怎么样,所以他需要你设计烹调方案使得美味指数最大

输入输出格式

输入格式:
第一行是两个正整数T和n,表示到达地球所需时间和食材个数。

下面一行n个整数,ai

下面一行n个整数,bi

下面一行n个整数,ci

输出格式:
输出最大美味指数

输入输出样例

输入样例#1:
74 1
502
2
47
输出样例#1:
408
说明

【数据范围】

对于40%的数据1<=n<=10

对于100%的数据1<=n<=50

所有数字均小于100,000

【题目来源】

tinylic改编

题解

01背包的升级,让性价比最高的先排

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;const int MAXN=105;const int MAXT=1e6+5;long long dp[MAXT];int T,n;struct node{long long a,b,c;}dat[MAXN];bool cmp(node x,node y){return x.b*y.c>y.b*x.c;}int main(){    scanf("%d%d",&T,&n);    for(int i=0;i<n;++i) scanf("%lld",&dat[i].a);    for(int i=0;i<n;++i) scanf("%lld",&dat[i].b);    for(int i=0;i<n;++i) scanf("%lld",&dat[i].c);    sort(dat,dat+n,cmp);    long long time=0;    memset(dp,-0x3f,sizeof(dp));    const long long INF=dp[0];    dp[0]=0;    for(int i=0;i<n;++i){        for(int j=T;j>=dat[i].c;--j){            if(dp[j-dat[i].c]>INF)            dp[j]=max(dp[j],dp[j-dat[i].c]+dat[i].a-j*dat[i].b);        }    }    long long ans=0;    for(int i=1;i<=T;++i) ans=max(ans,dp[i]);    printf("%lld\n",ans);    return 0;}
原创粉丝点击