【kuangbin计划】简单DP(1-3题 java/c++双语言详细解析)

本意是同时提供java以及c++两种语言的代码的题解

但是无奈oj网站一直欺负java语言慢, 因此本篇题解部分java代码只提供思路参考

不提供语言优化,有兴趣的同学可以自行优化java版本

过不了的java语言均已注明!

目录

4546. 最大和加强加强版 - 线性dp

java版 - 这个vjudge过不了 acw可以过

c++版

4547. 伊格内修斯和公主IV - 摩尔投票法

java版 - 超时tle 只提供思路

c++版 

4548. 猴子和香蕉 - 二维最长上升子序列dp


4546. 最大和加强加强版 - 线性dp

4546. 最大和加强加强版 - AcWing题库

题目:

思路:

定义 f[i][j][k] :前 i 个数,构成 j 个组,且第 i 个数是否在第 j 个组(0/1表示是否存在)

属性:最大值max

状态划分:

(1)选择第i个数

  • 第 i 个数在新开的组(前i-1个数已经构成j-1组)
  • f[i][j][1] = max( f[i-1][j-1][0],f[i-1][j-1][1] )+ w[i]
  • 第 i 个数在旧组(前i-1个数已经构成j组)
  • f[i][j][1] = max( f[i][j][1],f[i-1][j][1]+w[i] )

(2)不选择第i个数

  • f[i][j][0] = max( f[i-1][j][0],f[i-1][j][1] )

根据状态定义知答案为max(f[n][m][0],f[n][m][1])

因为i只与i-1层有关,所以可以进行优化&1

java版 - 这个vjudge过不了 acw可以过

import java.util.*;

class Main
{
    static int N=1000010,M=1010;
    static long[][][] f=new long[2][M][2]; //f[i][j][k]前i个数,分成j组,第i个数是否在第j组(0/1表示)
    static int[] w=new int[N];
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        while(sc.hasNextLine())
        {
            String[] s=sc.nextLine().split(" ");
            int m=Integer.parseInt(s[0]);
            int n=Integer.parseInt(s[1]);
            
            int cnt=2;
            for(int i=1;i<=n;i++) w[i]=Integer.parseInt(s[cnt++]);
            
            for(int i=0;i<2;i++)
                for(int j=0;j<M;j++) Arrays.fill(f[i][j],-0x3f3f3f3f);
                
            f[0&1][0][0]=0;
            
            for(int i=1;i<=n;i++)
            {
                f[i&1][0][0]=0;
                for(int j=1;j<=m;j++)
                {
                    //选择第i个数且第i个数在新开的组j (前i-1个数已经凑成j-1组)
                    f[i&1][j][1]=Math.max(f[i-1&1][j-1][1],f[i-1&1][j-1][0])+w[i];
                    //选择第i个数且第i个数插在旧组j最后 (前i-1个数已经凑成j组)
                    f[i&1][j][1]=Math.max(f[i&1][j][1],f[i-1&1][j][1]+w[i]);
                    //不选择第i个数 (前i-1个数已经凑成j组)
                    f[i&1][j][0]=Math.max(f[i-1&1][j][1],f[i-1&1][j][0]);
                }
            }
            System.out.println(Math.max(f[n&1][m][0],f[n&1][m][1]));
        }
    }
    
}

c++版

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <deque>
using namespace std;
#define int long long
#define max(a,b) (a>b?a:b)
#define io ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
const int N=1e6+10,M=1010;
int f[2][M][2];
int w[N];
int n,m;

signed main()
{
    io;
    while(cin>>m>>n)
    {
        for(int i=1;i<=n;i++) cin>>w[i];
        memset(f,-0x7f,sizeof f);
        
        f[0][0][0]=0;
        for(int i=1;i<=n;i++)
        {
            f[i&1][0][0]=0;
            for(int j=1;j<=m;j++)
            {
                f[i&1][j][1]=max(f[i-1&1][j-1][0],f[i-1&1][j-1][1])+w[i];
                f[i&1][j][1]=max(f[i&1][j][1],f[i-1&1][j][1]+w[i]);
                f[i&1][j][0]=max(f[i-1&1][j][1],f[i-1&1][j][0]);
            }
        }
        cout<<max(f[n&1][m][0],f[n&1][m][1])<<endl;
    }
}

4547. 伊格内修斯和公主IV - 摩尔投票法

4547. 伊格内修斯和公主IV - AcWing题库

题目:

思路: 

摩尔投票法的经典应用

如果有一个数x出现次数超过一半,则通过1v1打擂台的方式

与和x不同的数两两抵消后仍然存在

原因是因为x超过一半,其他数和它同归于尽后,最后生存下来的绝对是x

java版 - 超时tle 只提供思路

import java.util.*;

class Main
{
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        while(sc.hasNext())
        {
            int num=0,res=0;
            int n=sc.nextInt();
            while(n-->0)
            {
                int cur=sc.nextInt();
                if(res==0) //第一个人当擂主
                {
                    res=cur;
                    num=1;
                }else if(cur!=res)
                {
                    num--; //同归于尽
                    if(num==0) //如果擂主挂了 换新擂主
                    {
                        res=cur;
                        num=1;
                    }
                }else num++;
            }
            System.out.println(res);
        }
    }
}

c++版 

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <deque>
using namespace std;
#define int long long
#define io ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)

signed main()
{
    io;
    int n;
    while(cin>>n)
    {
        int num=0,res=0;
        while(n--)
        {
            int cur;
            cin>>cur;
        
            if(!res) //第一个直接当擂主
            {
                res=cur;
                num=1;
            }else if(res!=cur)
            {
                num--; //与挑战者同归于尽
                if(!num) res=cur,num=1; //如果擂主挂了换新擂主
            }else num++;
        }
        cout<<res<<endl;
    }
}

4548. 猴子和香蕉 - 二维最长上升子序列dp

4548. 猴子和香蕉 - AcWing题库

题目:

思路:

f[i]:前i个积木能堆起来的最大高度

先将每组数据的长宽高能摆放的所有情况存入数组

2种朝向×3种底面=6种摆放情况

按长宽从小到大排序,寻找满足 " 长和宽均递增 " 的最长上升子序列长度

不熟悉最长上升子序列dp模板的同学,可以学习一下:

【蓝桥杯集训26】线性DP(4 / 4)_Roye_ack的博客-CSDN博客

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <deque>
using namespace std;
//#define int long long
#define io ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
const int N=31*6; //一共n组数据 每组数据有2种朝向和3种底面
int f[N]; //f[i]表示前i个积木堆叠的最大高度
int n,m;

typedef struct 
{
    int x,y,z;
}cube;

bool cmp(cube a,cube b)
{
    if(a.x!=b.x) return a.x<b.x;
    return a.y<b.y;
}

signed main()
{
    io;
    int cnt=0;
    cube a[N];
    while(cin>>n&&n)
    {
        cnt++;
        for(int i=1;i<=n;i++)
        {
            int x,y,z;
            cin>>x>>y>>z;
            a[i].x=x,a[i].y=y,a[i].z=z;
            a[i+n].x=x,a[i+n].y=z,a[i+n].z=y;
            a[i+2*n].x=y,a[i+2*n].y=x,a[i+2*n].z=z;
            a[i+3*n].x=y,a[i+3*n].y=z,a[i+3*n].z=x;
            a[i+4*n].x=z,a[i+4*n].y=x,a[i+4*n].z=y;
            a[i+5*n].x=z,a[i+5*n].y=y,a[i+5*n].z=x;
            f[i]=0;
        }
        
        sort(a+1,a+1+n*6,cmp); //按长和宽从小到大排序
        
        int res=0;
        for(int i=1;i<=n*6;i++)
        {
            f[i]=a[i].z;
            for(int j=1;j<i;j++)
                if(a[i].x>a[j].x&&a[i].y>a[j].y)
                    f[i]=max(f[i],f[j]+a[i].z);
            res=max(res,f[i]);
        }
        cout<<"Case "<<cnt<<": maximum height = "<<res<<endl;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_61639349/article/details/129766778