PAT甲级--动态规划


如果看不懂的可以在开学之前留言哦!这段时间都在滴

A 1007 Maximum Subsequence Sum

思路:
最大连续子序列和:
(1)令状态dp[i]表示A[i]作为末尾的连续序列的最大和
(2)分情况讨论:
这个最大和的连续子序列只有一个元素:最大和=A[i];
这个最大和的连续子序列有多个元素,最大和是dp[i-1]+A[i];
于是可以得到状态转移方程:dp[i]=max{A[i],dp[i-1]+A[i]}
这个式子只和i与i之前的元素有关,且边界为dp[0]=A[0];.由此,从小到大枚举i,即可得到整个dp数组,其中最大值就是所要求的。

#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include<iostream>
#include<math.h>
using namespace std;
const int maxn=100010;
int a[maxn],dp[maxn];
int s[maxn]={0};//表示产生dp[i]的连续数列从a的哪一个元素开始


int main()
{
    int n,i;
    cin>>n;
    bool flag=false;//表示数组a中是否全小于0
    for(i=0;i<n;i++)
    {
       cin>>a[i];
       if(a[i]>=0)
        flag=true;
    }
    if(flag==false)
    {
        printf("0 %d %d",a[0],a[n-1]);
        return 0;
    }
        dp[0]=a[0];
        for(i=1;i<n;i++)
        {
            if(dp[i-1]+a[i]>a[i])
            {
                dp[i]=dp[i-1]+a[i];
                s[i]=s[i-1];
            }
            else
            {
                dp[i]=a[i];
                s[i]=i;
            }
        }
        int k=0;
        for(i=1;i<n;i++)
        {
            if(dp[i]>dp[k])
                k=i;
        }
     printf("%d %d %d\n",dp[k],a[s[k]],a[k]);
}

A 1045 Favorite Color Stripe

思路:
解法一:
最长不下降子序列:
(1)令d[i]表示以A[i]结尾的最长不下降子序列长度
(2)分情况讨论:
如果存在A[i]之前的元素A[j],使得A[j]<=A[i]且dp[j]+1>dp[i],则dp[i]=dp[j]+1;
如果A[i]之前的元素都比A[i]大,那么A[i]就只好自己形成一条链,长度为1
这一题要先把题目条件转换,通过hash转换,映射优先级,然后找最长不下降子序列

#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include<iostream>
#include<math.h>
using namespace std;
const int maxn=10010;
int a[maxn],dp[maxn];
int hashtable[maxn];//将喜欢的颜色映射

int main()
{
    int n,i,m,x;
    cin>>n>>m;
    for(i=1;i<=m;i++)
    {
       cin>>x;
       hashtable[x]=i;
    }
    int l,num=0;
    cin>>l;
    for(i=0;i<l;i++)
    {
        cin>>x;
        if(hashtable[x]>0)
        {
            a[num++]=hashtable[x];
        }
    }
    int ans=-1;
    for(i=0;i<num;i++)
    {
        dp[i]=1;
        for(int j=0;j<i;j++)
        {
            if(a[j]<=a[i]&&dp[i]<dp[j]+1)
                dp[i]=dp[j]+1;
        }
        ans=max(ans,dp[i]);
    }
    cout<<ans;
    return 0;
}

解法二:
最长公共子序列:
(1)令dp[i][j]表示字符串A的i号位和字符串B的j号位之前的最长公共子序列(下标从1开始)
(2)分情况讨论:
若A[i]==B[i],则字符串A与字符串B的最长公共子序列长多+1,即dp[i][j]=dp[i-1][j-1]+1;
若A[i]!=B[i],则字符串Ai号位与字符串B的j号位之前的最长公共子序列无法延长,由此dp[i-1][j]与dp[i][j-1]中的较大值。
由于此题要求的最长不下降,则公共序列中可以存在重复的元素,例如“ABBC”与“AABC”的最长公共子序列为“AABBC”。所以,dp[i-1][j]与dp[i][j-1]都是可以对dp[i][j]产生影响的。这样就可以知道:
若A[i]==B[i],则dp[i][j]应该是dp[i-1][j]与dp[i][j-1]中的较大值+1;
若A[i]!=B[i],则dp[i][j]应该是dp[i-1][j]与dp[i][j-1]中的较大值。
边界:dp[i][0]=dp[0][j]=0;

#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include<iostream>
#include<math.h>
using namespace std;
const int maxn=10010;
int a[maxn],b[maxn],dp[maxn][maxn];

int main()
{
    int n,i,m,x;
    cin>>n>>m;
    for(i=1;i<=m;i++)
    {
       cin>>a[i];
    }
    int l;
    cin>>l;
    //边界
    for(i=1;i<=l;i++)
    {
        cin>>b[i];
    }
    for(i=0;i<=m;i++)
    {
        dp[i][0]=0;
    }
     for(i=0;i<=l;i++)
    {
        dp[0][i]=0;
    }
    //状态转移方程
    for(i=1;i<=m;i++)
    {
        for(int j=1;j<=l;j++)
        {
            int maxc=max(dp[i-1][j],dp[i][j-1]);
            if(a[i]==b[j])
                dp[i][j]=maxc+1;
            else
                dp[i][j]=maxc;
        }
    }
    cout<<dp[m][l];
    return 0;
}

A 1040 Longest Symmetric String

思路:
最长回文子串:
(1)令dp[i][j]表示字符串s[i]至s[j]所表示的子串是否是回文子串,是则为1,不是则为0
(2)分情况讨论:
若s[i]==s[j],那么只要s[i+1]至s[j-1]是回文子串,s[i]至s[j]就是回文子串
若s[i]!=s[j],那么s[i]至s[j]就不是回文子串
边界:d[i][i]=1,dp[i][i+1]=(s[i]=s[i+1])?1:0

#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include<iostream>
#include<math.h>
using namespace std;
const int maxn=10010;
char s[maxn];
int dp[maxn][maxn];

int main()
{
    cin.getline(s,maxn);
    int len=strlen(s);
    int n,i,j,ans=1;
    for(i=1;i<len;i++)
    {
      dp[i][i]=1;
      if(i<len-1)
      {
          if(s[i]==s[i+1])
          {
              dp[i][i+1]=1;
              ans=2;
          }
      }
    }
    for(int l=3;l<=len;l++)
    {
        for(i=0;i+l-1<len;i++)
        {
            j=i+l-1;//子串的右端点
            if(s[i]==s[j]&&dp[i+1][j-1]==1)
            {
                dp[i][j]=1;
                ans=l;
            }
        }
    }
    cout<<ans;
    return 0;
}

A 1068 Find More Coins

思路:
01背包问题:
由于题目要求以价值从小到大的字典顺序输出,因此要先把数组从大到小排序,然后再求dp数组
(1)令dp[i][v]表示前i件物品恰好装入容量为v的背包中获得的价值
(2)分情况讨论:
若不放入第i件物品,dp[i][v]=dp[i-1][v];
若放入第i件物品,dp[i]=dp[i-1][v-w[i]]+c[i];
边界:dp[0][v]=0
针对此题,设置一个bool数组choose[i][v],用来记录计算dp[i][v]时是选择了哪个策略(放入=1),bool型数组flag记录得到的最大价值的方案中各件物品的选取情况。

#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include<iostream>
#include<math.h>
using namespace std;
const int maxn=10010;
int w[maxn],dp[maxn]={0};
bool choice[maxn][maxn],flag[maxn];

bool cmp(int a,int b)
{
    return a>b;
}

int main()
{
    int n,m,i,v;
    cin>>n>>m;
    for(i=1;i<=n;i++)
    {
      cin>>w[i];
    }
    sort(w+1,w+n+1,cmp);
    for(i=1;i<=n;i++)
    {
        for(v=m;v>=w[i];v--)
        {
            if(dp[v]<=dp[v-w[i]]+w[i])
            {
                dp[v]=dp[v-w[i]]+w[i];
                choice[i][v]=1;//放入第i件产品
            }
            else
            choice[i][v]=0;
        }
    }
        if(dp[m]!=m)
            printf("No Solution");
        else
        {
            int k=n,num=0,v=m;
            while(k>=0)
            {
                if(choice[k][v]==1)
                {
                    flag[k]=true;
                    v-=w[k];
                    num++;
                }
                else
                    flag[k]=false;
                    k--;
            }
            for(i=n;i>=1;i--)
            {
                if(flag[i]==true)
                {
                    cout<<w[i];
                    num--;
                    if(num>0)
                        printf(" ");
                }
            }

        }
    return 0;
}

补充几道题:

D - 棋力竞赛

题目
棋牌锦标赛即将开赛。棋迷小组准备参加比赛。小组统计出所有成员执黑棋和执白棋时的能力评分(评分为0到100的整数),小组会决定各成员是执黑参赛,还是执白参赛,还是不参赛。为了获得尽量好的总成绩,小组将分别选15人执黑棋,15人执白棋参赛。而比赛时每人只能参加1盘棋,小组希望所有参赛成员的能力评分之和最大,请你计算小组能派出队伍的最大能力总和。

数据输入
输入有多行,每行是两个0到100之间的整数由1个空格分隔,分别表示一个成员执黑棋时的能力评分和执白棋时的能力评分。
数据不少于30行,且不多于1000行。(用EOF判定输入结束)

数据输出
输出1行,一个数字,表示小组可以选出参赛成员的能力评分之和的最大值

测试样例
Input

93 62
74 80
70 72
87 84
66 78
86 94
93 87
72 90
78 63
60 91
77 64
77 91
87 73
69 62
80 68
81 83
74 63
86 68
53 80
59 73
68 70
57 94
88 85
99 75
71 66
77 64
81 92
74 57
71 63
82 97
76 100

Output

2520

代码
6分拿了4分

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<queue>
//#define ll long long
using namespace std;
const int maxn=110;

int vblack[maxn];
int vwhite[maxn];
int dp[maxn][maxn][maxn];


int main( )
{
    int i=0;
    int x,y;
    while(scanf("%d%d",&x,&y)!=EOF)
    {
        i++;
        vwhite[i]=x;
        vblack[i]=y;
    }
    int t=i;
    for(i=0;i<=15;i++)
    {
        for(int j=0;j<=15;j++)
        {
            dp[0][i][j] = 0;
        }

    }

    for(int i=1;i<=t;i++)
    {
        for(int v=0;v<=15;v++)
        {
            for(int k=0;k<=15;k++)
            {
                if(v+k>i)
                    continue;
                if(v>0&&k>0)
                    dp[i][v][k]=max(dp[i-1][v][k],max(dp[i-1][v-1][k]+vwhite[i],dp[i-1][v][k-1]+vblack[i]));
                else if(k>0)
                    dp[i][v][k]=max(dp[i-1][v][k],dp[i-1][v][k-1]+vblack[i]);
                else if(v>0)
                    dp[i][v][k]=max(dp[i-1][v][k],dp[i-1][v-1][k]+vwhite[i]);
                else
                    dp[i][v][k]=0;
            }

        }
    }
    cout<<dp[t][15][15];
	 return 0;
}

E - 牛吃草

问题描述
Alice在山上放牛,发现前方有一块矩形的优质草场。草场共有M×N个小格,每个格子有一棵牧草,用一个整数描述一棵牧草的品质。牛从左下角进入草场开始,吃完一个格子中的牧草之后,只能向右或向前继续吃,并且最后吃到草场的右上角。Alice希望牛能吃到的牧草的品质之和尽量大。请你帮Alice规划一下牛吃草的路线。

数据输入
第一行是由空格分开的两个整数M,N,表示草场的大小。 接下来有M行,每行中有N个数字,分别表示各格子中的牧草品质。
0 < N,M ≤ 100,牧草的品质是[0,30000]的整数

数据输出
用R表示向右走,用F表示向前走。在一行中输出从左下角出发,到右上角的一条路径,使得牛吃到的牧草品质之和最大。

样例
输入样例

2 3
3 2 4
1 5 1

输出样例

RFR

代码:
个人觉得很经典的动态规划的路径输出问题,可以学习路径输出的思路。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<stdio.h>
#include<stack>
#include<cmath>
#include<set>
//#define ll long long
using namespace std;
const int maxn=110;
const int INF=1000000000;

int a[maxn][maxn];
int dp[maxn][maxn];
char dir[maxn][maxn];
char path[maxn];

int main()
{
    int m,n,i,j;
    cin>>m>>n;
    for(i=0;i<m;i++)
    {
        for(j=0;j<n;j++)
        {
            cin>>a[i][j];
        }
    }

    dp[m-1][0]=a[m-1][0];
    for(i=m-2;i>=0;i--)
    {
        dp[i][0]=dp[i+1][0]+a[i][0];
        dir[i][0]='F';
    }
    for(j=1;j<n;j++)
    {
        dp[m-1][j]=dp[m-1][j-1]+a[m-1][j];
        dir[m-1][j]='R';
    }

    for(i=m-2;i>=0;i--)
    {
        for(j=1;j<n;j++)
        {
            if(dp[i+1][j]>dp[i][j-1])
            {
                dp[i][j]=dp[i+1][j]+a[i][j];
                dir[i][j]='F';
            }
            else
            {
                dp[i][j]=dp[i][j-1]+a[i][j];
                dir[i][j]='R';
            }
        }
    }

    i=0;
    j=n-1;
    int k=0;
     while(1)
    {
        if(dir[i][j]=='F')
        {
            path[k++]='F';
            i++;
        }
        else
        {
            path[k++]='R';
            j--;
        }
        if (i==m-1 && j==0)
            break;
    }
    while(k>0)
    {
        printf("%c",path[--k]);
    }
    printf("\n");
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值