zoukankan      html  css  js  c++  java
  • luogu P3147 USACO16OPEN dp好题

    luogu P3147 [USACO16OPEN]262144 P


    题意:

    给出 n 个正整数,\((2 \leq n \leq 262144)\),范围在 \(1- 40\) 内,选择相邻的两个相同的数,然后合并成一个比原来的大一的数,使得最大的数最大。

    如果不看数据范围的话,就是一道区间dp的板子题,但是我们发现 \(n\) 太大了,直接这么开二维数组是不行的,考虑缩小数组或者压缩状态。
    这里用到了倍增的思想来将第一维缩小。
    \(f[i][j]\) 表示左端点为 \(j\) 能够合并成 \(i\) 的右端点的位置。
    状态转移方程为:
    \(f[i][j]=max(f[i-1][f[i-1][j]])\)
    解释一下,我们先找到以 \(j\) 为左端点,能合并到 \(i-1\) 的位置,然后以这个位置为左端点,再合并 \(i-1\) 的位置,将这两个合并,就能合并出 \(i\) 了。
    为什么值要枚举到 \(58\)

    我们发现 \(2^{18}=262144\)

    所以值最大只有 \(58(40+18)\),然后这道题就能做了。

    /*
    *@Author:smyslenny
    *@Date:  2021.08.05
    *@Title: P3147 [USACO16OPEN]262144 P
    *@Main idea:f[i][j] 表示左端点为 j 能合并出 i 这个数的右端点的位置 
    f[i][j]=f[i-1][f[i-1][j]]
    */
    #include <bits/stdc+.h>
    using namespace std;
    const int M=3e5+5;
    int f[60][M],Ans,n;
    inline int read()
    {
    	register int x=0,y=1;
    	register char c=getchar();
    	while(!isdigit(c)) {if(c=='-') y=0;c=getchar();}
    	while(isdigit(c))  {x=x*10+(c^48);c=getchar();}
    	return y?x:-x;
    }
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;i++)
    		f[read()][i]=i+1;
    	for(int i=2;i<=58;i++)
    	{
    		for(int j=1;j<=n;j++)
    		{
    			if(!f[i][j])
    				f[i][j]=f[i-1][f[i-1][j]];
    			if(f[i][j])
    				Ans=i;
    		}
    	}
    	
    	printf("%d\n",Ans);
    	return 0;
    }
    
    
  • 相关阅读:
    JS数组分页
    UI框架
    mongodb
    koa2 router中间件的三种写法
    Float浮点数转二进制串和十六进制串
    Iterator和for...of循环
    mysql相关故障
    lsof
    iostat测试磁盘性能
    dd测试磁盘
  • 原文地址:https://www.cnblogs.com/jcgf/p/15103236.html
Copyright © 2011-2022 走看看