《程序设计基础II》实验6——动态规划

A - 递归的函数

Description

给定一个函数 f(a, b, c):

如果 a ≤ 0 或 b ≤ 0 或 c ≤ 0 返回值为 1;

如果 a > 20 或 b > 20 或 c > 20 返回值为 f(20, 20, 20);

如果 a < b 并且 b < c 返回 f(a, b, c−1) + f(a, b−1, c−1) − f(a, b−1, c);

其它情况返回 f(a−1, b, c) + f(a−1, b−1, c) + f(a−1, b, c−1) − f(a-1, b-1, c-1)。

看起来简单的一个函数?你能做对吗?

Input

输入包含多组测试数据,对于每组测试数据:

输入只有一行为 3 个整数a, b, c(a, b, c < 30)。

Output

对于每组测试数据,输出函数的计算结果。

Sample

Input

1 1 1
2 2 2

Output

2
4

Hint

#include <stdio.h>
#include <stdlib.h>
int p[21][21][21]= {0};
int f(int a,int b,int c)
{
    if(a<=0||b<=0||c<=0)
        return 1;
    else if(a>20||b>20||c>20)
    {
        if(p[20][20][20]==0)
            p[20][20][20]=f(20,20,20);
        return p[20][20][20];
    }
    else if(a<b&&b<c)
    {
        if(p[a][b][c]==0)
            p[a][b][c]=f(a,b,c-1)+f(a,b-1,c-1)-f(a,b-1,c);
    }
    else
    {
        if(p[a][b][c]==0)
            p[a][b][c]=f(a-1,b,c)+f(a-1,b-1,c)+f(a-1,b,c-1)-f(a-1,b-1,c-1);
        return p[a][b][c];
    }

}
int main()
{
    int a,b,c;
    while(~scanf("%d%d%d",&a,&b,&c))
    {
        printf("%d\n",f(a,b,c));
    }
    return 0;
}

B - 数字三角形问题

Description

给定一个由n行数字组成的数字三角形如下图所示。试设计一个算法,计算出从三角形的顶至底的一条路径,使该路径经过的数字总和最大。
 


对于给定的由n行数字组成的数字三角形,计算从三角形的顶至底的路径经过的数字和的最大值。

Input

输入数据的第1行是数字三角形的行数n,1≤n≤100。接下来n行是数字三角形各行中的数字。所有数字在0..99之间。

Output

输出数据只有一个整数,表示计算出的最大值。

Sample

Input 

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

Output 

30

Hint

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int a[200][200],D[200][200];
    int i,j;
    int n;
    scanf("%d",&n);
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=i; j++)
        {
            scanf("%d",&D[i][j]);
        }
    }
    for(i=1; i<=n; i++)
    {
        a[n][i]=D[n][i];
    }
    for(i=n; i>0; i--)
    {
        for(j=1; j<=i; j++)
        {
            if(a[i][j]>a[i][j+1])
            {
                a[i-1][j]=D[i-1][j]+a[i][j];
            }
            else a[i-1][j]=D[i-1][j]+a[i][j+1];

        }
    }
    printf("%d\n",a[1][1]);
    return 0;
}

C - 小鑫去爬山

Description

马上就要放假了,小鑫打算去爬山。
小鑫要去爬的这座山有n个海拔区间。为了清楚描述我们可以从上到下标号1到n。

第i个区间有i个落脚点,每一个落脚点都有一个危险值。

小鑫需要在第n个海拔区间挑选一个点向上爬,爬到第1个海拔区间(也就是山顶)。他必须规划一条路径,让危险值之和最小。这样才安全的。

并不是任意两个落脚点之间都可以相互到达。我们这样定义对于第i个(i<n)区间的第j个落脚点,只有第i+1个区间的第j个和第j+1个可以到达。

你能帮助他找到最安全的路么? 

Input

输入数据为多组,到文件结束。

对于每一组数据,第一行有一个数,为n 。n≤100;

接下来有n行,第i行有i个数。代表第i个区间i个落脚点的危险值。

所有数据均在int范围内。

Output

对于每组数据,输出一行一个数,为答案。

Sample

Input 

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

Output 

17

Hint

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int a[200][200],D[200][200];
    int i,j;
    int n;
    scanf("%d",&n);
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=i; j++)
        {
            scanf("%d",&D[i][j]);
        }
    }
    for(i=1; i<=n; i++)
    {
        a[n][i]=D[n][i];
    }
    for(i=n; i>0; i--)
    {
        for(j=1; j<=i; j++)
        {
            if(a[i][j]<a[i][j+1])
            {
                a[i-1][j]=D[i-1][j]+a[i][j];
            }
            else a[i-1][j]=D[i-1][j]+a[i][j+1];

        }
    }
    printf("%d\n",a[1][1]);
    return 0;
}

D - 最长公共子序列问题

Description

给定两个序列 X={x1,x2,…,xm} 和 Y={y1,y2,…,yn},找出X和Y的最长公共子序列。

Input

输入数据有多组,每组有两行 ,每行为一个长度不超过500的字符串(输入全是大写英文字母(A,Z)),表示序列X和Y。

Output

每组输出一行,表示所求得的最长公共子序列的长度,若不存在公共子序列,则输出0。

Sample

Input 

ABCBDAB
BDCABA

Output 

4
#include <stdio.h>
#include <stdlib.h>
int main()
{
    int a[200][200],D[200][200];
    int i,j;
    int n;
    scanf("%d",&n);
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=i; j++)
        {
            scanf("%d",&D[i][j]);
        }
    }
    for(i=1; i<=n; i++)
    {
        a[n][i]=D[n][i];
    }
    for(i=n; i>0; i--)
    {
        for(j=1; j<=i; j++)
        {
            if(a[i][j]<a[i][j+1])
            {
                a[i-1][j]=D[i-1][j]+a[i][j];
            }
            else a[i-1][j]=D[i-1][j]+a[i][j+1];

        }
    }
    printf("%d\n",a[1][1]);
    return 0;
}

E - 最长上升子序列

Description

一个数的序列bi,当b1 < b2 < ... < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, ..., aN),我们可以得到一些上升的子序列(ai1, ai2, ..., aiK),这里1<= i1 < i2 < ... < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8)。

你的任务,就是对于给定的序列,求出最长上升子序列的长度。

Input

输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。

Output

最长上升子序列的长度。

Sample

Input 

7
1 7 3 5 9 4 8

Output 

4
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
    int d[1001],a[1001];
    int n,i,j,m=0;
    while(~scanf("%d",&n))
    {
        for(i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
        }
        for(i=1; i<=n; i++)
        {
            d[i]=a[i];
            for(j=1; j<i; j++)
            {
                if(a[j]<a[i])
                {
                    if(d[i]<d[j]+a[i])
                        d[i]=d[j]+a[i];
                }
            }
            if(d[i]>m)
            {
                m=d[i];
            }
        }
        printf("%d\n",m);
    }
    return 0;
}

F - 上升子序列

Description

一个只包含非负整数的序列bi,当b1 < b2 < ... < bS的时候,我们称这个序列是上升的。对于给定的一个序列{a1, a2, ...,aN},我们可以得到一些上升的子序列{ai1, ai2, ..., aiK},这里1 ≤ i1 < i2 <...< iK ≤ N。例如:对于序列{1, 7, 3, 5, 9, 4, 8},有它的一些上升子序列,如{1, 7}, {3, 4, 8}等等。这些子序列中序列和最大的是子序列{1, 3, 5, 9},它的所有元素的和为18。

对于给定的一个序列,求出它的最大的上升子序列的和。

注意:最长的上升子序列的和不一定是最大的哦。

Input

输入包含多组测试数据,对于每组测试数据:

输入数据的第一行为序列的长度 n(1 ≤ n ≤ 1000),

第二行为n个非负整数 b1,b2,...,bn(0 ≤ bi ≤ 1000)。

Output

对于每组测试数据,输出其最大上升子序列的和。

Sample

Input 

7
1 7 3 5 9 4 8

Output 

18

Hint

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
    int d[1001],a[1001];
    int n,i,j,m=0;
    while(~scanf("%d",&n))
    {
        for(i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
        }
        for(i=1; i<=n; i++)
        {
            d[i]=a[i];
            for(j=1; j<i; j++)
            {
                if(a[j]<a[i])
                {
                    if(d[i]<d[j]+a[i])
                        d[i]=d[j]+a[i];
                }
            }
            if(d[i]>m)
            {
                m=d[i];
            }
        }
        printf("%d\n",m);
    }
    return 0;
}

G - 最长公共子序列

Description

从一个给定的串中删去(不一定连续地删去)0个或0个以上的字符,剩下地字符按原来顺序组成的串。例如:“ ”,“a”,“xb”,“aaa”,“bbb”,“xabb”,“xaaabbb”都是串“xaaabbb”的子序列。(例子中的串不包含引号。)
 
编程求N个非空串的最长公共子序列的长度。限制:2<=N<=100;N个串中的字符只会是数字0,1,…,9或小写英文字母a,b,…,z;每个串非空且最多含100个字符;N个串的长度的乘积不会超过30000。

Input

文件第1行是一个整数T,表示测试数据的个数(1<=T<=10)。接下来有T组测试数据。各组测试数据的第1行是一个整数Ni,表示第i组数据中串的个数。各组测试数据的第2到N+1行中,每行一个串,串中不会有空格,但行首和行末可能有空格,这些空格当然不算作串的一部分。

Output

输出T行,每行一个数,第i行的数表示第i组测试数据中Ni个非空串的最长公共子序列的长度。

Sample

Input 

1
3
ab
bc
cd

Output 

0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char a[110][110];
int d[30010];
int len[110];
int n;
int f(int *x)
{
    int i,j,index,ret,t;
    for(i=1; i<=n; i++)
    {
        if(x[i]==0)
        {
            return 0;
        }
    }
    for(index=x[n]-1,i=n-1; i>=1; i--)
    {
        index=index*len[i]+x[i]-1;
    }
    if(d[index]>=0)
    {
        return d[index];
    }
    for(i=2; i<=n; i++)
    {
        if(a[1][x[1]-1]!=a[i][x[i]-1])
        {
            break;
        }
    }
    if(i>n)
    {
        for(j=1; j<=n; j++)
        {
            x[j]--;
        }
        ret=f(x)+1;
        for(j=1; j<=n; j++)
        {
            x[j]++;
        }
    }
    else
    {
        ret=0;
        for(j=1; j<=n; j++)
        {
            x[j]--;
            t=f(x);
            if(t>ret)
            {
                ret=t;
            }
            x[j]++;
        }
    }
    d[index]=ret;
    return ret;
}
int main()
{
    int T,i;
    int l[110];
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(i=1; i<=n; i++)
        {
            scanf("%s",a[i]);
            len[i]=strlen(a[i]);
            l[i]=strlen(a[i]);
        }
        memset(d,-1,sizeof(d));
        printf("%d\n",f(l));
    }
    return 0;
}

H - 取数字问题

Description

给定M×N的矩阵,其中的每个元素都是-10到10之间的整数。你的任务是从左上角(1,1)走到右下角(M,N),每一步只能够向右或者向下,并且不能够走出矩阵的范围。你所经过的方格里面的数字都必须被选取,请找出一条最合适的道路,使得在路上被选取的数字之和是尽可能小的正整数。

Input

输入第1行是两个整数M和N,(2<=M<=10,2<=N<=10),分别表示矩阵的行和列的数目。接下来M行,每行包括N个整数,就是矩阵中的每一行的N个元素。

Output

输出只有一行,就是一个整数,表示所选道路上数字之和所能达到的最小正整数。如果不能达到任何正整数,输出-1。

Sample

Input 

2 2
0 2
1 0

Output 

1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int max(int a,int b)
{
    return a>b? a:b;
}
int n,m;
int ans=0x3f3f3f3f,mp[11][11];
void dfs(int i,int j,int sum)
{
    sum+=mp[i][j];
    if(i<m-1)
        dfs(i+1,j,sum);
    if(j<n-1)
        dfs(i,j+1,sum);
    if(i==m-1&&j==n-1&&sum>0&&sum<ans)
        ans=sum;
}
 
int main()
{
 
    scanf("%d %d",&m,&n);
    for(int i=0; i<m; i++)
        for(int j=0; j<n; j++)
            scanf("%d",&mp[i][j]);
    dfs(0,0,0);
    if(ans==0x3f3f3f3f)
        printf("-1");
    else
    printf("%d\n",ans);
    return 0;
}

I - 免费馅饼

Description

都说天上不会掉馅饼,但有一天gameboy正走在回家的小径上,忽然天上掉下大把大把的馅饼。说来gameboy的人品实在是太好了,这馅饼别处都不掉,就掉落在他身旁的10米范围内。馅饼如果掉在了地上当然就不能吃了,所以gameboy马上卸下身上的背包去接。但由于小径两侧都不能站人,所以他只能在小径上接。由于gameboy平时老呆在房间里玩游戏,虽然在游戏中是个身手敏捷的高手,但在现实中运动神经特别迟钝,每秒种只有在移动不超过一米的范围内接住坠落的馅饼。现在给这条小径如图标上坐标: 
 



 为了使问题简化,假设在接下来的一段时间里,馅饼都掉落在0-10这11个位置。开始时gameboy站在5这个位置,因此在第一秒,他只能接到4,5,6这三个位置中期中一个位置上的馅饼。问gameboy最多可能接到多少个馅饼?(假设他的背包可以容纳无穷多个馅饼)

Input

输入数据有多组。每组数据的第一行为以正整数n(0 < n < 100000),表示有n个馅饼掉在这条小径上。在结下来的n行中,每行有两个整数x,T(0 <= T < 100000),表示在第T秒有一个馅饼掉在x点上。同一秒钟在同一点上可能掉下多个馅饼。n=0时输入结束。
 

Output

每一组输入数据对应一行输出。输出一个整数m,表示gameboy最多可能接到m个馅饼。

提示:本题的输入数据量比较大,建议用scanf读入,用cin可能会超时。

Sample

Input 

6
5 1
4 1
6 1
7 2
7 2
8 3
0

Output 

4

Hint

hdoj1176 有链接提示的题目请先去链接处提交程序,AC后提交到SDUTOJ中,以便查询存档。

#include<stdio.h>
#include<stdlib.h>
int sz[15][100005]={0};
#define max(a,b) (((a)>(b))?(a):(b))
int main() {
    int bl1, bl2, bl3;
    int a, b;
    int i=0;
    for (;scanf("%d", &bl1) != EOF && bl1 != 0;) {
        i = 0;
        memset(sz, 0, sizeof(sz));
        for (bl2 = 0;bl2 < bl1;bl2++) {
            scanf("%d", &a);
            scanf("%d", &b);
            sz[a][b]++;
            i = max(i, b);
        }
        for (bl2 = i - 1;bl2 >= 0;bl2--) {
            for (bl3 = 10;bl3 >= 0;bl3--) {
                if (bl3 == 0) {
                    sz[bl3][bl2] += max(sz[bl3][bl2 + 1], sz[bl3 + 1][bl2 + 1]);
                }
                else {
                    sz[bl3][bl2] += max(sz[bl3][bl2 + 1], max(sz[bl3 - 1][bl2 + 1], sz[bl3 + 1][bl2 + 1]));
                }
            }
        }
        printf("%d\n", sz[5][0]);
    }
    return 0;
}

J - 走迷宫

Description

有一个m*n格的迷宫(表示有m行、n列),其中有可走的也有不可走的,如果用1表示可以走,0表示不可以走,输入这m*n个数据和起始点、结束点(起始点和结束点都是用两个数据来描述的,分别表示这个点的行号和列号)。现在要你编程找出所有可行的道路,要求所走的路中没有重复的点,走时只能是上下左右四个方向。如果一条路都不可行,则输出相应信息(用-1表示无路)。

Input

第一行是两个数m,n(1< m, n< 15),接下来是m行n列由1和0组成的数据,最后两行是起始点和结束点。

Output

所有可行的路径,输出时按照左上右下的顺序。描述一个点时用(x,y)的形式,除开始点外,其他的都要用“->”表示。如果没有一条可行的路则输出-1。

Sample

Input 

5 4
1 1 0 0
1 1 1 1
0 1 1 0
1 1 0 1
1 1 1 1
1 1
5 4

Output 

(1,1)->(1,2)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4)
(1,1)->(1,2)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4)
(1,1)->(1,2)->(2,2)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4)
(1,1)->(1,2)->(2,2)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4)
(1,1)->(2,1)->(2,2)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4)
(1,1)->(2,1)->(2,2)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4)
#include<stdio.h>
#include<string.h>
 
 
struct node
{
    int x, y;
} ls[1000];
 
 
int dx[] = {0,-1,0,1}, dy[] = {-1,0,1,0};
int bj[16][16], map[16][16];
int m, n, step, sum = 0, xz, yz;
 
 
void dfs(int x1, int y1)
{
    int i;
    if(x1 == xz && y1 == yz)
    {
        sum++;
        for(i = 0; i < step; i++)
        {
            printf("(%d,%d)",ls[i].x,ls[i].y);
            if(i < step - 1)
                printf("->");
        }
        printf("\n");
    }
    else
    {
        int kx, ky;
        for(i = 0; i < 4; i++)
        {
            kx  = x1 + dx[i];
            ky =  y1 + dy[i];
            if(kx >= 1 && ky >= 1 && kx <= n && ky <= m && !bj[kx][ky] && map[kx][ky])
            {
                ls[step].x = kx;
                ls[step].y = ky;
                step++;
                bj[kx][ky] = 1;
                dfs(kx,ky);
                bj[kx][ky] = 0;
                step--;
            }
        }
    }
}
 
int main()
{
    int i, j,xq,yq;
    while(~scanf("%d%d",&n,&m))
    {
        memset(bj,0,sizeof(bj));
        memset(ls,0,sizeof(ls));
        for(i = 1; i <= n; i++)
            for(j = 1; j <= m; j++)
                scanf("%d",&map[i][j]);
        scanf("%d%d%d%d",&xq,&yq,&xz,&yz);
        ls[0].x = xq;
        ls[0].y = yq;
        sum = 0;
        step = 1;
        bj[xq][yq] = 1;
        dfs(xq,yq);
        if(sum == 0)
            printf("-1\n");
    }
    return 0;
}
 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值