【luogu 3146/3147】248/262144

【原题题面】

【luogu 3146】248传送门

【luogu 3147】262144传送门

【题面大意】

给定一个1*N(2<=N<=248/262144) 的地图,每次可以合并相邻两个(数值范围1-40),问最大能合出多少。注意合并后的数值并非加倍而是+1,例如2与2合并后的数值为3。

【题解】

【壹/248】

区间dp。

f[l][r]表示从l到r的合并后最大值,注意合并时必须满足f[l][k]==f[k+1][r]。

初值为f[i][i] = a[i],因为要求max所以其余赋值为-inf或0也行。

答案为每个f[l][r]取max的值。

【code1】

#include<bits/stdc++.h>
using namespace std;
#define File "248"
#define ll long long
inline void file(){
    freopen(File".in","r",stdin);
    freopen(File".out","w",stdout);
}
inline int read(){
    int x=0,f=1;   char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
    return x*f;
}
const int mxn = 2e3+3;
int n,mx;
int a[mxn];
int f[mxn][mxn];
inline int MAX(int x,int y){
    return x < y ? y : x;
}
/*
1.初值
2.循环范围
3.转移
4.答案
*/
int main(){
//    file();
    n = read();
    for(register int i = 1;i <= n; ++i) a[i] = read(),mx = MAX(mx,a[i]);
    memset(f,0,sizeof f);
    for(int i = 1;i <= n; ++i) f[i][i] = a[i];

    for(register int len = 2;len <= n; ++len){
        for(register int l = 1;l <= n-len+1; ++l){
            int r = l+len-1;
            for(register int k = l;k < r; ++k)
                if(f[l][k]==f[k+1][r]) f[l][r] = MAX(f[l][r],f[l][k]+1);
            mx = MAX(mx,f[l][r]);
        }
    }
    printf("%d\n",mx);
    return 0;
}
/*
4
1
1
1
2
*/
View Code

【贰/261244】

仔细确认发现跟前一题确确实实是一样的,n^3的区间dp当然是死翘翘了。

观察题目的特性:

序列每个数的范围极其小,所以可以考虑用它来搞一波事情。

考虑dp状态的设置:
一维是能合并出来的数字的大小
一维是下标的值
如果将f值设为能合并出来的数的大小->无法转移,不满足最优子结构

将f值设置为下标,类似倍增。
f[i][j]表示左端点为j时,能合并出的最大值为i的区间的右端点。
这样设置状态能保证往后转移。
转移:f[i][j] = f[i-1][f[i-1][j]];

初值:f[a[i]][i] = i+1;

答案:i的值。

关于58:

因为给定的原序列最大值为40,极限情况下,相邻两数的合并每次会给最大值加一,那么最多会加到58。

当然循环到59,60...什么的也没问题,如果时间复杂度过得去的话...

【code2】

#include<bits/stdc++.h>
using namespace std;
#define File "262144"
#define ll long long
inline void file(){
    freopen(File".in","r",stdin);
    freopen(File".out","w",stdout);
}
inline int read(){
    int x=0,f=1;   char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
    return x*f;
}
const int mxn = (1<<18)+5;
int n;
int f[63][mxn];
/*
1.转移->f[i][j] = f[i-1][f[i-1][j]]
2.循环
3.初值
4.答案
*/
int ans;
int main(){
    //file();
    n = read();
    for(int i = 1;i <= n; ++i){
        int t = read();
        f[t][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 = max(i,ans);
        }
    }
    printf("%d\n",ans);
    return 0;
}
View Code
posted @ 2019-06-03 16:29  ve-2021  阅读(283)  评论(0编辑  收藏  举报