洛谷P3190 [HNOI2007] 神奇游乐园WA80求调(插头dp实在太毒瘤)

P3190 [HNOI2007] 神奇游乐园

题目描述

经历了一段艰辛的旅程后,主人公小 P 乘坐飞艇返回。在返回的途中,小 P 发现在漫无边际的沙漠中,有一块狭长的绿地特别显眼。往下仔细一看,才发现这是一个游乐场,专为旅途中疲惫的人设计。

娱乐场可以看成是一块大小为 n\times m 的区域,且这个 n\times m 的区域被分成 n\times m 个小格子,每个小格子中就有一个娱乐项目。

然而,小 P 并不喜欢其中的所有娱乐项目,于是,他给每个项目一个满意度。满意度为正时表示小 P 喜欢这个项目,值越大表示越喜欢。为负时表示他不喜欢,这个负数的绝对值越大表示他越不喜欢。为 0 时表示他对这个项目没有喜恶。

小 P 决定将飞艇停在某个小格中,然后每步他可以移动到相邻的上下左右四个格子的某个格子中。

小 P 希望找一条路径,从飞艇所在格出发,最后又回到这个格子。

小 P 有一个习惯,从不喜欢浪费时间。因此,他希望经过每个格子都是有意义的:他到一个地方后,就一定要感受以下那里的惊险和刺激,不管自己是不是喜欢那里的娱乐项目。而且,除了飞艇所在格,其他的格子他不愿意经过两次。小 P 希望自己至少要经过四个格子。

在满足这些条件的情况下,小 P 希望自己玩过的娱乐项目的满意度之和最高。你能帮他找到这个最高的满意度之和吗?

输入格式

第一行为两个正整数 nm,表示游乐场的大小为 n\times m

接下来的 n 行,每行有 m 个整数,第 (i + 1) 行的第 j 个数表示游乐场的第 i 行第 j 列的小格子中的娱乐项目的满意度 a_{i,j}。同一行的两个整数之间用空格隔开。

输出格式

仅一行为一个整数,表示最高的满意度之和。

输入输出样例 #1

输入 #1

4 4
100 300 -400 400
-100 1000 1000 1000
-100 -100 -100 -100
-100 -100 -100 1000

输出 #1

4000

说明/提示

数据规模与约定

对于 100\% 的数据,保证 2\leq n\leq 1002\leq m\leq 6-10^3\leq a_{i,j}\leq 10^3

#include<bits/stdc++.h>
using namespace std;
int n,m,a[200][10],dp[2][40000],th[40000][110],idx;//dp用括号序列,四进制存三进制状态(因为四进制方便)
int erhui;
int s[100];
void check()//特判
{
	int cnt=-1e9;
	for(int i=1;i<n;i++)
	{
		for(int j=1;j<m;j++)
		{
			cnt=max(cnt,a[i][j]+a[i+1][j]+a[i][j+1]+a[i+1][j+1]);
		}
	}
	if(cnt<0)
	{
		cout<<cnt;
		erhui=1;
		return;
	}
}
int main()
{
	cin>>n>>m;
	int x,cnt,u;
	for(int i=0;i<(1<<2*m+2);i++)//预处理,th[][100]存三进制合法状态,th[][x]存三进制状态中与第x位的插头匹配的插头
	{
		cnt=0,u=1;
		for(int j=10;j>=0;j--)
		{
			x=(i>>2*j)&3;
			if(x==3)
			{
				u=0;
				break;
			}
			if(x<2)
			{
				cnt+=x;
				continue;
			}
			if(cnt--==0)
			{
				u=0;
				break;
			}
		}
		if(u==1&&cnt==0)
		{
			cnt=0;
			th[++idx][100]=i;
			for(int j=10;j>=0;j--)
			{
				x=(i>>2*j)&3;
				if(x==0)continue;
				if(x==1)
				{
					s[++cnt]=2*j;
					continue;
				}
				th[idx][2*j]=s[cnt];
				th[idx][s[cnt]]=2*j;
				cnt--;
			}
		}
	}
	memset(dp,-0x3f,sizeof dp);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>a[i][j];
		}
	}
	check();
	if(erhui)
	{
		return 0;
	}
	dp[0][0]=0;
	dp[0][6<<2*m-2]=a[1][1];
	int xb=1,xb1,kk,c,op;
	for(int i=1;i<=n;i++)//用当前位更新后续状态
	{
		for(int j=1;j<=m;j++)
		{
			if(i==n&&j==m)
			{
				break;
			}
			xb1=xb;
			xb^=1;
			memset(dp[xb1],-0x3f,sizeof dp[xb1]);
			for(int id=1,k;id<=idx;id++)
			{
				k=th[id][100];
				if(j!=m)
				{
					c=dp[xb][k]+a[i][j+1];
					op=(k>>2*(m-j-1))&15;//当前位的操作符,如当前位为12时,操作符=1*4+2=6,当前位为10时,操作符=1*4+0=6,当前位为21时,操作符=2*4+1=9(四进制存三进制状态)
					kk=(op<<2*(m-j-1))^k;//将当前位操作符的位置置0后的值(方便使用位运算给该位切换操作符)
					if(op==0)//无插头
					{
						dp[xb1][kk]=max(dp[xb][k],dp[xb1][kk]);//下一位没插头
						dp[xb1][kk^(6<<2*(m-j-1))]=max(c,dp[xb1][kk^(6<<2*(m-j-1))]);//下一位一个左插头,一个右插头
					}
					if(op==1||op==4)//一个=1的插头
					{
						dp[xb1][kk^(1<<2*(m-j-1))]=max(c,dp[xb1][kk^(1<<2*(m-j-1))]);//下方放一个=1的插头
						dp[xb1][kk^(4<<2*(m-j-1))]=max(c,dp[xb1][kk^(4<<2*(m-j-1))]);//右侧放一个=1的插头
					}
					if(op==2||op==8)//一个=2的插头
					{
						dp[xb1][kk^(2<<2*(m-j-1))]=max(c,dp[xb1][kk^(2<<2*(m-j-1))]);//下方放一个=2的插头
						dp[xb1][kk^(8<<2*(m-j-1))]=max(c,dp[xb1][kk^(8<<2*(m-j-1))]);//右侧放一个=2的插头
					}
					if(op==5)//当前位11
					dp[xb1][kk^(3<<th[id][2*(m-j-1)])]=max(c,dp[xb1][kk^(3<<th[id][2*(m-j-1)])]);//合并,找到右插头匹配
					if(op==10)//当前位22
					dp[xb1][kk^(3<<th[id][2*(m-j)])]=max(c,dp[xb1][kk^(3<<th[id][2*(m-j)])]);//合并,找到左插头匹配
					if(op==9)//当前位21
					dp[xb1][kk]=max(c,dp[xb1][kk]);//直接合并,左右插头自动匹配
				}
				else
				{
					if(k&3)
					{
						continue;
					}
					c=dp[xb][k]+a[i+1][1];
					op=(k>>2*m)&3;
					kk=k^(op<<2*m);
					kk>>=2;
					if(op==0)
					{
						dp[xb1][kk]=max(dp[xb][k],dp[xb1][kk]);
						dp[xb1][kk^(6<<2*m-2)]=max(c,dp[xb1][kk^(6<<2*m-2)]);
					}
					if(op==1)
					{
						dp[xb1][kk^(1<<2*m-2)]=max(c,dp[xb1][kk^(1<<2*m-2)]);
						dp[xb1][kk^(4<<2*m-2)]=max(c,dp[xb1][kk^(4<<2*m-2)]);
					}
					if(op==2)
					{
						dp[xb1][kk^(2<<2*m-2)]=max(c,dp[xb1][kk^(2<<2*m-2)]);
						dp[xb1][kk^(8<<2*m-2)]=max(c,dp[xb1][kk^(8<<2*m-2)]);
					}
				}
			}
		}
	}
	cout<<max(dp[xb][6]+a[n][m],dp[xb][0]);//可以没有插头,或者一个左插头,一个右插头
}
1 个赞

严老师,再帮我看看呗

1 个赞

这次好像不是数组溢出的问题

1 个赞

也没见祖宗

1 个赞

有兴趣的也可以帮我看看,求了

1 个赞

不用了,没有判断在中途封口的情况,即op==6

1 个赞

已经AC了

1 个赞

不能自己给自己解决方案 @王峥睿