C++之动态规划复习

来源:互联网 发布:c语言基础教程pdf 编辑:程序博客网 时间:2024/06/07 11:15

C++之动态规划总复习

声明:这些题只讲思路,若有需求,可在评论回复。

【基础算法】 书的复制

时间限制: 1 Sec
内存限制: 64 MB

题目描述
现在要把m本有顺序的书分给k个人复制(抄写),每一个人的抄写速度都一样,一本书不允许给两个(或以上)的人抄写,分给每一个人的书,必须是连续的,比如不能把第一、第三、第四本书给同一个人抄写。 现在请你设计一种方案,使得复制时间最短复制时间为抄写页数最多的人用去的时间

输入
第一行两个整数m,k;(k≤m≤500) 第二行m个整数,第i个整数表示第i本书的页数。

输出
共k行,每行两个整数,第i行表示第i个人抄写的书的起始编号和终止编号。k行的起始编号应该从小到大排列,如果有多解,则尽可能让前面的人少抄写

样例输入

9 31 2 3 4 5 6 7 8 9

样例输出

1 56 78 9

分析
因为题目中提到’m本有顺序的书分给k个人’,所以,我们可以设f[i][j]表示的是:把j本书分给i个人抄写所需的最短时间。值得注意的是:每个DP的这种数组都需要赋值。那赋值为什么呢?其实,在题目中我们不难发现:有一个值一定是固定的——一个人抄j本书的时间。由此,我们可以得到f[1][j]的初值了(0-0 需要输入)。而不是f[1][j]的数的初始值就为10000000(极大值),因为在题目中我们有看到’复制时间’最短,而它也要进行比较,自然取最大的。(便于比较,不会出错)和谁比较?自然是左边和右边。如图;即状态转移方程式为:f[k][m]=min{max{f[k-1][i],d[j]-d[l]},f[i][j]}.

k=2;①        ①1 | 2 3 4 5 6 7 8 91 2 | 3 4 5 6 7 8 91 2 3 | 4 5 6 7 8 9 1 2 3 4 | 5 6 7 8 9 ....1 2 3 4 5 6 7 8 | 9 k=3;②        ①1 2 | 3 4 5 6 7 8 91 2 3 | 4 5 6 7 8 9 1 2 3 4 | 5 6 7 8 9 ....1 2 3 4 5 6 7 8 | 9 k=4;③        ①1 2 3 | 4 5 6 7 8 9 1 2 3 4 | 5 6 7 8 9 ....1 2 3 4 5 6 7 8 | 9 

而至于最后的输出方案数,我们可以用贪心的方法来实现(当然,也可以用二分来实现)。因为我们已经求得用时(最优值T),所以第k个人,他们每个人最多T页。所以从最后一本书开始逆序分配给k个人抄写(尽可能让前面的人少抄写)。然后是k-1,k-2……

【基础算法】挖地雷

题目描述
在一个地图上有N个地窖,依次编号为1..N,每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径
例如在下图中:

这里写图片描述

上图表示,地窖1可以挖到地窖2,地窖3;地窖2可以挖到地窖4;地窖3可以挖到地窖4,地窖5;地窖4可以挖到地窖5. 当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,然后可以沿着某条路径接着往下挖(仅能选择一条路径),当某个地窖无连接时挖地雷工作结束
试设计一个挖地雷的方案,使某人能挖到最多的地雷

输入
第1行:1个整数N(1<=N<=20),表示地窖的个数
第2行:N个用空格分开的整数,第i个整数表示地窖i里所埋的地雷个数
第3行~N+1行:第i+2行有n-i个用空格分开的整数,第i+2行描述第i个地窖与第i+1,i+2,…,n个地窖的连接情况。某个整数为1,表示有路径相连,为0,表示无路径相连。

输出
第1行:1个整数,表示能挖到的最多的地雷数量
第2行:若干个用空格分开的整数,表示所挖的地窖编号。编号按所挖地窖的顺序输出,若有多个方案都得到最多的地雷数量,则输出编号尽可能小的一组。

样例输入

510 8 4 7 61  1  0  00  1  01  1 1

样例输出

311 2 4 5

题目分析
其实,这道题目可以从后往前去看(不要问我为什么)。如此,则边界为f[n]=w[n],w[i]为第i个地窖所藏有的地雷数,a[i][j]表示第i地窖与第j个地窖是否有通路,f[i]为从第i个地窖开始最多可以挖出的地雷数。在我看来,其实这道题就是一个最长上升子序列的改版。我们不难发现,文题中的最大地雷数可以看做最长长度。由此,我们便可以以相同的方法来完成此题。但是,值得注意的是,我们在寻找f[j]后的最大地雷数时,还需要判断这条路走不走得通。

【NOIP2000提高】乘积最大

时间限制: 1 Sec
内存限制: 64 MB

题目描述
今年是国际数学联盟确定的“2000——世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加。活动中,主持人给所有参加活动的选手出了这样一道题目:
设有一个长度N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大
同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:有一个数字串: 312,当N=3,K=1时会有以下两种分法: 1)3*12=36 2)31*2=62 这时,符合题目要求的结果是: 31*2=62
现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。

输入
第1行:共有2个自然数N,K (6<=N<=40,1<=K<=6)
第2行:一个长度为N的数字串

输出
第1行:最大乘积

样例输入

4 21231

样例输出

62

提示

注意数据规模,答案可能会超过int范围

题目分析
面对此题,我们可以用它插入的乘号数来划分阶段,即插入k个乘号,可看做k个阶段的决策的问题。设f[i][k]表示在前i位数中插入k个乘号的最大值,a[i][j]表示从i位到j位所组成的自然数。即可得:f[i][k]=max{f[j][k-1]*a[j+1][i]} (k<=j)(j<i)边界值为:f[j][0]=a[1][j].

【基础算法】0-1背包问题

时间限制: 1 Sec
内存限制: 64 MB

题目描述
有 n 件物品, 每件物品有一个价值和一个重量,分别记为: b1,b2, …bn w1,w2, …wn 其中所有的 重量wi 均为整数。 现有一个背包,其最大载重量为W,要求从这n件物品中任取若干件(这些物品要么被装入要么被留下)。问背包中装入哪些物品可使得所装物品的价值和最大?

输入
第1行:2个整数n(1<=n<=1000)和W(1<=W<=10000),分别表示物品的件数和背包的最大载重量。
第2-n+1行:每行2个用空格分开的整数,第i+1行的整数表示第i件物品的重量wi和价值bi(1<=bi,wi<=10000)。

输出
第1行:1个整数,表示背包所能装下的物品的最大总价值
第2-?行:每行3个用空格分开的整数,i, wi, bi,分别表示最优解中的物品的编号、重量和价值。

样例输入

4 52 33 44 55 6

样例输出

71 2 32 3 4

题目分析
虽说这道题是一道简单的DP题,但我们还是要拿来说一说。(毕竟它还是挺经典的呢)。在这里,我们可以设f[i][v]表示前i件物品,总重量不超过v的最优价值。则f[i][v]=max{f[i-1][v-w[i]]+c[i],f[i-1][v]},如此,则f[n][m]为最优解。但这样的做法还是有问题,万一它的数据再大一点呢?所以,滚动数组就出现了。我们可以设f[v]为不超过v斤的最大价值。则f[v]=max{f[v],f[v-w[i]]+c[i]}(当v>=w[i],i<=i<=n时)。

【基础算法】硬币问题

时间限制: 1 Sec
内存限制: 64 MB

题目描述
有N种硬币,面值分别为V1,V2,…,Vn。每种都有无限多。给定非负整数S,可以选用多少个硬币,使得面值之和恰好为S?输出硬币数目的最大值和最小值

输入
第1行:2个整数N(1<=N<=100, 0 <=S <= 10000)
第2行:N个空格分开的整数,表示每种硬币的面值。(1 <= Vi <= S) 数据保证所有的Vi均不相同。

输出
第1行:1个整数,表示币值之和恰好为S的硬币数目的最大值。如果不能达到S,输出-1。
第2行:1个整数,表示币值之和恰好为S的硬币数目的最小值。如果不能达到S,输出-1。

样例输入

2 103 8

样例输出

-1-1

题目分析
这道题,是一道典型的完全背包问题。
口水都干了,直接上代码。

#include<bits/stdc++.h> using namespace std; int f1[10005],f2[10005],a[101]; int main() {     int n,m;     scanf("%d%d",&n,&m);     for(int i=1;i<=n;i++)     scanf("%d",&a[i]);     for(int i=0;i<=m;i++){f2[i]=100000;f1[i]=-100000;}     f1[0]=0;f2[0]=0;     for(int i=1;i<=n;i++)         for(int j=0;j<=m;j++)             if(a[i]<=j)             {                 f1[j]=max(f1[j],f1[j-a[i]]+1);                 f2[j]=min(f2[j],f2[j-a[i]]+1);             }     if(f1[m]<0||f2[m]<0)printf("-1\n-1\n");     else printf("%d\n%d\n",f1[m],f2[m]); } 

我们大家一定要有信心,我们一定能看懂的。-__-,好啦,先就这样,后面还会陆续复习的。也希望同学们都要养成复习的习惯喔。

原创粉丝点击