您的位置:首页 > 其它

动态规划

2016-05-30 14:09 155 查看

一 递推求解

1.题目1205:N阶楼梯上楼问题

时间限制:1 秒内存限制:128 兆特殊判题:否提交:4325解决:1742

题目描述:

N阶楼梯上楼问题:一次可以走两阶或一阶,问有多少种上楼方式。(要求采用非递归)

输入:

输入包括一个整数N,(1<=N<90)。

输出:

可能有多组测试数据,对于每组数据,

输出当楼梯阶数是N时的上楼方式个数。

样例输入:

4

样例输出:

5

来源:

2008年华中科技大学计算机保研机试真题

#include<stdio.h>

//保存登台阶的种类数
long long F[91]={0};

int main(){

//台阶数
int cnt=0;
F[1]=1;
F[2]=2;

//1.预处理,求出登台阶种类数
for(int i=3;i<=90;i++){
F[i]=F[i-1]+F[i-2];
}
//2.输入台阶数
while(scanf("%d",&cnt)!=EOF){
//3.输出结果
printf("%lld\n",F[cnt]);
}

return 0;
}


注意:

使用long long保存大数

输出long long用%lld

2.题目1451:不容易系列之一

时间限制:1 秒内存限制:128 兆特殊判题:否提交:1355解决:791

题目描述:

大家常常感慨,要做好一件事情真的不容易,确实,失败比成功容易多了!

做好“一件”事情尚且不易,若想永远成功而总从不失败,那更是难上加难了,就像花钱总是比挣钱容易的道理一样。

话虽这样说,我还是要告诉大家,要想失败到一定程度也是不容易的。比如,我高中的时候,就有一个神奇的女生,在英语考试的时候,竟然把40个单项选择题全部做错了!大家都学过概率论,应该知道出现这种情况的概率,所以至今我都觉得这是一件神奇的事情。如果套用一句经典的评语,我们可以这样总结:一个人做错一道选择题并不难,难的是全部做错,一个不对。

不幸的是,这种小概率事件又发生了,而且就在我们身边:

事情是这样的——HDU有个网名叫做8006的男性同学,结交网友无数,最近该同学玩起了浪漫,同时给n个网友每人写了一封信,这都没什么,要命的是,他竟然把所有的信都装错了信封!注意了,是全部装错哟!

现在的问题是:请大家帮可怜的8006同学计算一下,一共有多少种可能的错误方式呢?

输入:

输入数据包含多个多个测试实例,每个测试实例占用一行,每行包含一个正整数n(1《n<=20),n表示8006的网友的人数。

输出:

对于每行输入请输出可能的错误方式的数量,每个实例的输出占用一行。

样例输入:

2

3

样例输出:

1

2

#include<stdio.h>

//保存不同题目数的全错误数
long long num[21]={0};

int main(){
//输入的题目数
int cnt=0;
num[1]=0;
num[2]=1;

//1.预处理求出前20错误数目
for(int i=3;i<=20;i++){
num[i]=(i-1)*num[i-1]+(i-1)*num[i-2];
}
//2.输入要计算的题目数
while(scanf("%d",&cnt)!=EOF){
//3.输出
printf("%lld\n",num[cnt]);
}

return 0;
}


二 最长递增子序列( LIS)

1.题目1112:拦截导弹

时间限制:1 秒内存限制:32 兆特殊判题:否提交:3704解决:1818

题目描述:

某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹。拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。

输入:

每组输入有两行,

第一行,输入雷达捕捉到的敌国导弹的数量k(k<=25),

第二行,输入k个正整数,表示k枚导弹的高度,按来袭导弹的袭击时间顺序给出,以空格分隔。

输出:

每组输出只有一行,包含一个整数,表示最多能拦截多少枚导弹。

样例输入:

8

300 207 155 300 299 170 158 65

样例输出:

6

来源:

2007年北京大学计算机研究生机试真题

#include<stdio.h>

//保存导弹高度
int dh[26]={0};
//保存以第i个导弹为结尾,能够制导的个数
int dcnt[26]={0};

//求最大值函数
int getMax(int a,int b){
return a>b?a:b;
}

int main(){
//导弹数目
int cnt=0;
//1.输入导弹数目
while(scanf("%d",&cnt)!=EOF){
//2.输入每个导弹的高度
for(int i=0;i<cnt;i++){
scanf("%d",dh+i);
}
//3.计算最大的拦截数
for(int i=0;i<cnt;i++){
//局部最大值
int tmax=1;
for(int j=0;j<i;j++){
if(dh[j]>=dh[i]){
tmax=getMax(tmax,dcnt[j]+1);
}
}
dcnt[i]=tmax;
}
//4.输出结果
int ans=0;
for(int i=0;i<cnt;i++){
if(ans<dcnt[i]){
ans=dcnt[i];
}
}
printf("%d\n",ans);
}
return 0;
}


三 最长公共子序列( LCS)

1.题目1042:Coincidence

时间限制:1 秒内存限制:32 兆特殊判题:否提交:2796解决:1506

题目描述:

Find a longest common subsequence of two strings.

输入:

First and second line of each input case contain two strings of lowercase character a…z. There are no spaces before, inside or after the strings. Lengths of strings do not exceed 100.

输出:

For each case, output k – the length of a longest common subsequence in one line.

样例输入:

abcd

cxbydz

样例输出:

2

来源:

2008年上海交通大学计算机研究生机试真题

#include<stdio.h>
#include<string.h>
using namespace std;

//比较大小函数
int getMax(int a,int b){
return a>b?a:b;
}

//保存l1的前i,l2的前j的最大字串数目
int dp[101][101]={0};

int main(){

//保存输入字符串
char l1[100];
char l2[100];

//1.输入两个字符串
while(scanf("%s %s",l1,l2)!=EOF){
//字符串的长度
int l1_le=strlen(l1);
int l2_le=strlen(l2);
//清空
for(int i=0;i<=l1_le;i++){
dp[i][0]=0;
}
for(int j=0;j<=l2_le;j++){
dp[0][j]=0;
}

//2.计算最大子字符串的长度
for(int i=1;i<=l1_le;i++){
for(int j=1;j<=l2_le;j++){
//如果第i 第j个字符相同
if(l1[i-1]==l2[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
}
//如果第i 第j个字符不同
else{
dp[i][j]=getMax(dp[i-1][j],dp[i][j-1]);
}
}
}
//3.输出结果
printf("%d\n",dp[l1_le][l2_le]);

}
return 0;
}


注意:

数组下标从1开始,要谨慎使用。但有时使用却很方便。

五 动态规划问题分析举例

1.题目1452:搬寝室

时间限制:1 秒内存限制:128 兆特殊判题:否提交:1614解决:656

题目描述:

搬寝室是很累的,xhd深有体会.时间追述2006年7月9号,那天xhd迫于无奈要从27号楼搬到3号楼,因为10号要封楼了.看着寝室里的n件物品,xhd开始发呆,因为n是一个小于2000的整数,实在是太多了,于是xhd决定随便搬2*k件过去就行了.但还是会很累,因为2*k也不小是一个不大于n的整数.幸运的是xhd根据多年的搬东西的经验发现每搬一次的疲劳度是和左右手的物品的重量差的平方成正比(这里补充一句,xhd每次搬两件东西,左手一件右手一件).例如xhd左手拿重量为3的物品,右手拿重量为6的物品,则他搬完这次的疲劳度为(6-3)^2 = 9.现在可怜的xhd希望知道搬完这2*k件物品后的最佳状态是怎样的(也就是最低的疲劳度),请告诉他吧。

输入:

每组输入数据有两行,第一行有两个数n,k(2<=2*k<=n<2000).第二行有n个整数分别表示n件物品的重量(重量是一个小于2^15的正整数).

输出:

对应每组输入数据,输出数据只有一个表示他的最少的疲劳度,每个一行.

样例输入:

2 1

1 3

样例输出:

4

#include<stdio.h>
#include<algorithm>
using namespace std;

//求最小值函数
int getMin(int a,int b){
return a>b?b:a;
}
//保存n个物品
int siz[2000]={0};
//爆粗疲劳度
int pl[1001][2001]={0};

int main(){
//一共的物品数目n,希望搬k对
int n=0;
int k=0;

//1.输入n,k
while(scanf("%d %d",&n,&k)!=EOF){
//2.输入n个物品
for(int i=0;i<n;i++){
scanf("%d",siz+i);
}
//3.进行排序(升序)
sort(siz,siz+n);
//4.求出每个状态的疲劳度
for(int i=1;i<=k;i++){
for(int j=2*i;j<=n;j++){
if(j==2*i){
pl[i][j]=pl[i-1][j-2]+(siz[j-1]-siz[j-2])* (siz[j-1]-siz[j-2]);
}else{
pl[i][j]=getMin(pl[i][j-1],pl[i-1][j-2]+(siz[j-1]-siz[j-2])* (siz[j-1]-siz[j-2]));
}
}
}
//5.输出结果
printf("%d\n",pl[k]
);
}
return 0;
}


2.题目1453:Greedy Tino(未AC)

时间限制:1 秒内存限制:128 兆特殊判题:否提交:969解决:312

题目描述:

Tino wrote a long long story. BUT! in Chinese…

So I have to tell you the problem directly and discard his long long story. That is tino want to carry some oranges with “Carrying pole”, and he must make two side of the Carrying pole are the same weight. Each orange have its’ weight. So greedy tino want to know the maximum weight he can carry.

输入:

The first line of input contains a number t, which means there are t cases of the test data.

for each test case, the first line contain a number n, indicate the number of oranges.

the second line contains n numbers, Wi, indicate the weight of each orange

n is between 1 and 100, inclusive. Wi is between 0 and 2000, inclusive. the sum of Wi is equal or less than 2000.

输出:

For each test case, output the maximum weight in one side of Carrying pole. If you can’t carry any orange, output -1. Output format is shown in Sample Output.

样例输入:

1

5

1 2 3 4 5

样例输出:

Case 1: 7

#include<stdio.h>
#define OFFSET 2000
#define INF 0x7fffffff

//得到最大值
int getMax(int a,int b,int c){
if(a>b)
return a>c?a:c;
else
return b>c?b:c;
}

int main(){
//保存testcase的个数
int t=0;
//保存橘子的个数
int s_cnt=0;
//保存平衡橘子的数组
int dp[101][4001]={0};
//保存每个句子的数量
int wei[100];
//保存是否有重量为0的橘子
bool haveZero=false;

//1.输入测试用例的个数
scanf("%d",&t);
for(int k=0;k<t;k++){
haveZero=false;
//2.输入当前测试用例的橘子数目
scanf("%d",&s_cnt);

int cnt=0;
//3.输入每个橘子的重量
for(int i=0;i<s_cnt;i++){
scanf("%d",wei+cnt);
if(wei[cnt]==0){
cnt--;
haveZero=true;
}
cnt++;
}
s_cnt=cnt;

//4.初始化平衡橘子的数组为负无穷,不能初始化为0,同时设置dp[0][0]为0
for(int i=-2000;i<=2000;i++)
dp[0][i+OFFSET]=-INF;
dp[0][0+OFFSET]=0;
//5.计算平衡橘子数组
for(int i=1;i<=s_cnt;i++){
//用来循环重量-2000--2000
for(int j=-2000;j<=2000;j++){
//保证j+OFFSET-wei[i]与j+OFFSET+wei[i]在-2000--2000范围内
int tem1=-INF;
int tem2=-INF;
if(j-wei[i-1]>=-2000&&dp[i-1][j+OFFSET-wei[i-1]]!=-INF){
tem1=dp[i-1][j+OFFSET-wei[i-1]]+wei[i-1];
}
if(j+wei[i-1]<=2000&&dp[i-1][j+OFFSET+wei[i-1]]!=-INF){
tem2=dp[i-1][j+OFFSET+wei[i-1]]+wei[i-1];
}

a5ae
dp[i][j+OFFSET]=getMax(tem1,tem2,dp[i-1][j+OFFSET]);//加在第一框,加在第二框,两边都不加
}
}
//6.输出结果
printf("Case %d:",k+1);
if(dp[s_cnt][0+OFFSET]==0){
if(haveZero==true){
printf("0\n");
}else{
printf("-1\n");
}
}else{
printf("%d\n",dp[s_cnt][0+OFFSET]/2);
}
}
return 0;
}


注意事项:

初始化橘子差不能初始化为0,因为需要考虑橘子质量为0的情况。

因为递归需要有i-1的情况,所以为了简单,循环变量需要从1开始。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: