第四周博客总结

一、动态规划模块
1.最长上升子序列
注意:这个子序列(subsequence)和子段(subsegment)是有区别的,后者是连续的

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
int a[5000];
int dp[5000];
int main()
{
   int n;
   int max=-2;
   cin>>n;
   for(int i=1;i<=n;i++)
    cin>>a[i];
   for(int i=1;i<=n;i++)
    dp[i]=1;
   for(int i=2;i<=n;i++)
   {
       for(int j=1;j<i;j++)
       {
           if(a[i]>a[j]&&dp[j]+1>dp[i])
           {
               dp[i]=dp[j]+1;
           }
       }
       if(dp[i]>max)
        max=dp[i];
   }
   cout<<max<<endl;
    return 0;
}

深度思考
(1)题目描述设有由n(1≤n≤200)个不相同的整数组成的数列,记为:b(1)、b(2)、……、b(n)若存在i1<i2<i3<…<ie 且有b(i1)<=b(i2)<=…<=b(ie)则称为长度为e的不下降序列。程序要求,当原数列出之后,求出最长的不下降序列。

例如13,7,9,16,38,24,37,18,44,19,21,22,63,15。例中13,16,18,19,21,22,63就是一个长度为7的不下降序列,同时也有7 ,9,16,18,19,21,22,63组成的长度为8的不下降序列。
输入
第一行为n,第二行为用空格隔开的n个整数。
输出
第一行为输出最大个数max(形式见样例);

第二行为max个整数形成的不下降序列,答案可能不唯一,输出一种就可以了,本题进行特殊评测。

#include <iostream>
#include <cmath>
using namespace std;
int a[500];
int dp[500];
int b[500];
int main()
{
    int n,max=-1;
    int k;
    cin>>n;
    for(int i=1; i<=n; i++)
        cin>>a[i];
    for(int i=1; i<=n; i++)
        dp[i]=1;
    for(int i=2; i<=n; i++)
    {
        for(int j=1; j<i; j++)
        {
            if(a[i]>=a[j]&&(dp[j]+1>dp[i]))
               {
                   dp[i]=dp[j]+1;
               }
            if(dp[i]>max)
            {
                max=dp[i];
                k=i;//最长不下降子序列的最后一个元素位置
            }
        }
    }
   cout<<"max="<<max<<endl;
    /*本质就是对上面操作的逆序*/
    int maxs,u;int c=0;
//u作为遍历元素
    maxs=max;
    u=k-1;

    b[++c]=k;//a[b[i]]
    while(maxs>1)
    {
        if(dp[u]==maxs-1&&a[k]>=a[u])
        {
            b[++c]=u;
            maxs--;
            k=u;//如果这里是k--,这是行不通的
            u--;
        }
        else u--;
    }
for(int i=max;i>=1;i--)
    cout<<a[b[i]]<<" ";

    return 0;
}

(2)题目描述某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数,导弹数不超过1000),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入:
输入导弹依次飞来的高度。
输出:
第一行:最多能拦截的导弹数;//就是最长下降子序列

第二行:要拦截所有导弹最少要配备的系统数。//就是最长不下降子序列

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
int a[5000];
int b[2500];
int dp[5000];
int main()
{
   //ios::sync_with_stdio(false);
    int max=-1,maxe=-1;
    int n,t=1;
    while(scanf("%d",&n)!=EOF)
    {
        a[t]=n;
        t++;
    }
    for(int i=1; i<=t-1; i++)
        dp[i]=1;
    for(int i=2; i<=t-1; i++)
    {
        for(int j=1; j<i; j++)
        {
            if(a[i]<=a[j]&&dp[j]+1>dp[i])
            {
                dp[i]=dp[j]+1;
            }
        }
        if(dp[i]>max)
           {   max=dp[i];
           }
    }
    cout<<max<<endl;
    memset(dp,0,sizeof(dp));
     for(int i=1; i<=t-1; i++)
        dp[i]=1;
    for(int i=2;i<=t-1;i++)
    {
        for(int j=1;j<i;j++)
        {
            if(a[i]>a[j]&&dp[j]+1>dp[i])
                dp[i]=dp[j]+1;
        }
        if(dp[i]>maxe)
            maxe=dp[i];
    }
    cout<<maxe<<endl;
    return 0;
}

2.最长公共子序列个数

#include<iostream>
using namespace std;
char a[N],b[N];
int f[N][N];
int main()
{
    scanf("%s%s",&a[1],&b[1]);
    int len_a=strlen(&a[1]);
    int len_b=strlen(&b[1]);
    
    for(int i=0;i<=len_a;i++)
        f[i][0]=0;
    for(int i=0;i<=len_b;i++)
        f[0][i]=0;
    for(int i=1;i<=len_a;i++)
        for(int j=1;j<=len_b;j++)
            if(a[i]==b[j])
                f[i][j]=f[i-1][j-1]+1;
            else 
                f[i][j]=max(f[i-1][j],f[i][j-1]);
    
    cout<<f[len_a][len_b]<<endl;
    return 0;
}

3.最大子段和

int max = 0;
for(int i = 1; i <= n; i ++){
   if(dp[i-1] > 0){
        dp[i] = dp[i-1] + A[i];
   }else{
        dp[i] = A[i];
   }//dp[i]=max(dp[i-1]+a[i],a[i])
   if(dp[i]> max){
        max = dp[i];
   }
}
//时间复杂度为O(n)

4.最大子矩阵和

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define INF 0x3f3f3f3f
#define N 1001
using namespace std;
int a[N][N],f[N];
int maxArray(int t[],int n)//这个函数作用是对这个列向量进行遍历,至到找到最大值
    int sum=0,maxx=-INF;
    for(int i=1; i<=n; i++)
    {
        if(sum>0)
            sum+=t[i];
        else
            sum=t[i];
        if(sum>maxx)
            maxx=sum;
    }
    return maxx;
}
int main()
{
    int n;
    cin>>n;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            cin>>a[i][j];

    int maxx=-INF;//最小的
    for(int i=1; i<=n; i++)
    {
        memset(f,0,sizeof(f));
        for(int j=i; j<=n; j++)
        {
            for(int k=1; k<=n; k++)
                f[k]+=a[j][k];//把一个子矩阵化成一个列向量

            int temp=maxArray(f,n);
            if(temp>maxx)
                maxx=temp;//总体找最大值
        }
    }

    cout<<maxx<<endl;

    return 0;
}

5.poj_2479_Maximum sum(最大两子段和)
题意
在一段数中找出两个连续子段,使其和最大。
分析:
可以联想到求最大子段和的方法,此题就是以此为基础的。

先从左向右做一遍求最大子段和的DP,得到数组dp1[ ];

再从右向左做一遍求最大子段和的DP,得到数组dp2[ ]。

然后更新一下这两个数组,更新方法为:

对于数组dp1[ ],若dp1[i-1]>dp1[i],dp[i]=dp1[i-1],否则不变;

对于数组dp2[ ],若dp2[i+1]>dp2[i],dp[i]=dp2[i+1],否则不变。

以样例为例:

                   1 -1 2 2 3 -3 4 -4 5 -5

得到

dp1----------->1 1 2 4 7 7 8 8 9 9

dp2----------->9 9 9 7 5 5 5 5 5 -5

最后枚举1~n-1,求dp1[i]+dp2[i+1]的最大值,即为答案。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int dp1[50050],dp2[50050],a[50050];
int main(){
    int n,t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        dp1[0]=0;
        for(int i=1;i<=n;i++){
            if(dp1[i-1]>0)
                dp1[i]=dp1[i-1]+a[i];
            else dp1[i]=a[i];
        }
        dp2[n+1]=0;
        for(int i=n;i>0;i--){
            if(dp2[i+1]>0)
                dp2[i]=dp2[i+1]+a[i];
            else dp2[i]=a[i];
        }
        for(int i=2;i<=n;i++){
            if(dp1[i]<dp1[i-1]) dp1[i]=dp1[i-1];
        }
        for(int i=n-1;i>=1;i--){
            if(dp2[i]<dp2[i+1]) dp2[i]=dp2[i+1];
        }
        int bigsum=dp1[1]+dp2[2];
        for(int i=1;i<n;i++){
            bigsum=max(bigsum,dp1[i]+dp2[i+1]);
        }
        printf("%d\n",bigsum);
    }
    return 0;
}

6.复制书稿
【题目描述】
现在要把m本有顺序的书分给k个人复制(抄写),每一个人的抄写速度都一样,一本书不允许给两个(或以上)的人抄写,分给每一个人的书,必须是连续的,比如不能把第一、第三和第四本书给同一个人抄写。

现在请你设计一种方案,使得复制时间最短。复制时间为抄写页数最多的人用去的时间。

【输入】
第一行两个整数m,k;(k≤m≤500)

第二行m个整数,第i个整数表示第i本书的页数。

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

【输入样例】
9 3
1 2 3 4 5 6 7 8 9

【输出样例】
1 5
6 7
8 9

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<queue>
#include<vector>
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define N 101
#define MOD 2520
#define E 1e-12
using namespace std;
int a[N],b[N],c[N][N];
int sum[N],f[N][N];
int main()
{
    int m,n;
    cin>>m>>n;
    for(int i=1;i<=m;i++)
    {
        cin>>a[i];
        sum[i]=sum[i-1]+a[i];
    }
 
    sum[0]=0;
    memset(f,INF,sizeof(f));
    for(int i=1;i<=m;i++)
        f[1][i]=sum[i];
 
    //分段取大,状态取小
    for(int i=2;i<=n;i++)
        for(int j=1;j<=m;j++)
            for(int k=i-1;k<j;k++)
                f[i][j]=min(f[i][j],max(f[i-1][k],sum[j]-sum[k]));
 
    int maxx=f[n][m];
    int temp=n;
    for(int i=m;i>0;i--)
    {
        if(a[i]+b[temp]>maxx)
            temp--;
        b[temp]+=a[i];
        c[temp][++c[temp][0]]=i;
    }
 
    for(int i=1;i<=n;i++)
        cout<<c[i][c[i][0]]<<" "<<c[i][1]<<endl;
    return 0;
}

2.cf上的题
题目来源(点击这里)
ac代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int a,b;
        cin>>a>>b;
        int n=a+b;
        string s;
        cin>>s;
        for(int i=0; i<n; i++)
        {
            if(s[i]=='?')s[i]=s[n-i-1];
        }
        a-=count(s.begin(),s.end(),'0');
        b-=count(s.begin(),s.end(),'1');

        for(int i=0; i<=n/2; i++)
        {
            if(s[i]=='?' and i!=n-i-1)
            {
                if(a>1)
                {
                    s[i]=s[n-i-1]='0';
                    a-=2;
                }
                else if(b>1)
                {
                    s[i]=s[n-i-1]='1';
                    b-=2;
                }

            }
            else if(s[i]=='?')
            {
                if(a)
                {
                    s[i]='0';
                    a--;
                }
                else
                {
                    s[i]='1';
                    b--;
                }
            }
        }
        string ans=s;
        reverse(ans.begin(),ans.end());
        if(ans==s and a==b and b==0)cout<<s<<endl;
        else cout<<"-1"<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沉梦昂志️

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值