「区间DP」「洛谷PP3146 」[USACO16OPEN]248 G

[USACO16OPEN]248 G

题目:

题目描述

Bessie likes downloading games to play on her cell phone, even though she doesfind the small touch screen rather cumbersome to use with her large hooves.

She is particularly intrigued by the current game she is playing.The game starts with a sequence of NNN positive integers (2≤N≤2482 leq Nleq 2482≤N≤248), each in the range 1…401 ldots 401…40. In one move, Bessie cantake two adjacent numbers with equal values and replace them a singlenumber of value one greater (e.g., she might replace two adjacent 7swith an 8). The goal is to maximize the value of the largest numberpresent in the sequence at the end of the game. Please help Bessiescore as highly as possible!

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

输入格式

The first line of input contains NNN, and the next NNN lines give the sequence

of NNN numbers at the start of the game.

输出格式

Please output the largest integer Bessie can generate.

输入输出样例

输入 #1

4
1
1
1
2

输出 #1

3

说明/提示

In this example shown here, Bessie first merges the second and third 1s to

obtain the sequence 1 2 2, and then she merges the 2s into a 3. Note that it is

not optimal to join the first two 1s.

思路:

受区间DP题单的影响,上来想分成两种状态分别讨论,向左向右预处理合并,之后区间DP割点合并。

代码实现如下:

/*#!/bin/sh
dir=$GEDIT_CURRENT_DOCUMENT_DIR
name=$GEDIT_CURRENT_DOCUMENT_NAME
pre=${name%.*}
g++ -O2 $dir/$name -o $pre -g -Wall -std=c++11
if test $? -eq 0; then
    gnome-terminal -x bash -c "time $dir/$pre;echo;read;"
fi*/
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=505,INF=0x3f3f3f3f;
int n,f[2][maxn][maxn],a[maxn],m,ans;//1左0右
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int main(){
	n=read();
	for(int i=1;i<=n;i++){a[i]=read(),f[0][i][i]=f[1][i][i]=a[i],ans=max(ans,a[i]);}//预处理f[0][i][i]和f[1][i][i]
	for(int i=1;i<n;i++){
		if(a[i]==a[i+1])f[0][i][i+1]=f[1][i][i+1]=a[i]+1;//相等合并
		else f[0][i][i+1]=a[i+1],f[1][i][i+1]=a[i];
		ans=max(ans,max(f[0][i][i+1],f[1][i][i+1]));
		//cout<<i<<" "<<i+1<<" "<<f[0][i][i+1]<<endl;
	}//预处理f[0][i][i+1]和f[1][i][i+1]
	for(int d=3;d<=n;d++){
		for(int i=1,j;(j=i+d-1)<=n;i++){
			if(f[0][i][j-1]==a[j])f[0][i][j]=a[j]+1;//能合并就合并
			else f[0][i][j]=a[j];//不能合并要改为最右端的数否则无法继续向右合并
			if(f[1][i+1][j]==a[i])f[1][i][j]=a[i]+1;//同理
			else f[1][i][j]=a[i];
			ans=max(ans,max(f[1][i][j],f[0][i][j]));
			//cout<<i<<" "<<j<<" "<<f[0][i][j]<<endl;
		}
	}//向右向左合并预处理
	for(int d=2;d<=n;d++){//左右合并
		for(int i=1,j;(j=i+d-1)<=n;i++){
			for(int k=i;k<j;k++){
				if(f[0][i][k]==f[1][k+1][j])f[0][i][j]=f[1][i][j]=f[0][i][k]+1;//两个区间内的数值相等就可以合并,因为保证了断点左右数值一定代表左右区间的数值,这也是用两个状态的原因
			//	else f[0][i][j]=f[]
				ans=max(ans,max(f[1][i][j],f[0][i][j]));
			}
		}
	}
	cout<<ans;
}

调了二十分钟扔了,望有缘人能调出来。

后来康了一眼题解发现想多了。其实不用两个状态,而且只用上面代码的最后一个合并就行

断点左右区间数值相等就合并,价值+1,否则就为0

代码:

/*#!/bin/sh
dir=$GEDIT_CURRENT_DOCUMENT_DIR
name=$GEDIT_CURRENT_DOCUMENT_NAME
pre=${name%.*}
g++ -O2 $dir/$name -o $pre -g -Wall -std=c++11
if test $? -eq 0; then
    gnome-terminal -x bash -c "time $dir/$pre;echo;read;"
fi*/
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=505,INF=0x3f3f3f3f;
int n,f[maxn][maxn],a[maxn],m,ans;//f[i][j]表示合并i-j的价值,当然如果i-j内不能合并,就为0,例如样例中f[3][4]=0
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int main(){
//freopen("a.in","r",stdin);
	n=read();
	for(int i=1;i<=n;i++){a[i]=read(),f[i][i]=a[i],ans=max(ans,a[i]);}
	for(int i=1;i<n;i++){
		if(a[i]==a[i+1])f[i][i+1]=a[i]+1;
	//	else f[i][i+1]=0;
		ans=max(ans,f[i][i+1]);
		//cout<<i<<" "<<i+1<<" "<<f[0][i][i+1]<<endl;
	}//预处理一模一样
	for(int d=2;d<=n;d++){
		for(int i=1,j;(j=i+d-1)<=n;i++){
			for(int k=i;k<j;k++){//有k+1右端点肯定不能超过j
				if(f[i][k]==f[k+1][j]&&f[i][k]!=0)f[i][j]=f[i][k]+1;//如果两个区间i-k和k+1-j内的数值相同,那就合并。
                                                                                    //但是有一要求:两个区间内的数值不能为0,即两个区间必须本身能合并
				ans=max(ans,f[i][j]);
				
			}
			//cout<<i<<" "<<j<<" "<<f[i][j]<<endl;
		}
	}
	cout<<ans;
}

over~

原文地址:https://www.cnblogs.com/614685877--aakennes/p/13183082.html