这是一个很菜的 Oier 的博客|

Hanx16Msgr

园龄:2年8个月粉丝:12关注:3

2022-08-15 15:42阅读: 42评论: 0推荐: 0

P3190 [HNOI2007]神奇游乐园

[HNOI2007]神奇游乐园

Luogu P3190

题目描述

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

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

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

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

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

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

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

输入格式

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

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

输出格式

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

样例 #1

样例输入 #1

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

样例输出 #1

4000

提示

数据规模与约定

对于 100% 的数据,保证 2n1002m6103ai,j103

Solution

可以说是模板题的另一种形式了,做法基本和模板题一致,只有少部分地方需要更改。如果你对插头 DP 还不熟,那写一写这道题也是个不错的选择(插头 DP 的黑题还是有很多水题的)(推销下我写的插头DP的博客)。

与模板题不同的是,此题的回路并不要求走完整个方格图,这也就是说每个格子具有的插头可能有 2 个或者是 0 个。那么知道了这点就可以来尝试讨论一下各种情况了。

此题解使用括号表示法,如果不会的话上面的博客链接有请

分类讨论

1. 没有左插和上插

此种情况下,当前格子可以没有任何插头(也就是说不走这个格子),也可以同时具有右插和下插(作为一条路径的拐点):

0000

或者是

0021

2. 只有左插

此时当前格子可以有一个下插或者右插:

01/21/20

或者是

01/201/2

此时的右插或下插应该与左插的编号保持一致。

3. 只有下插

与上述情况几乎相同,就不详细再写了。

4. 左插上插都为 1

如果把 1 的插头看做是进入当前格子,2 的插头看做走出当前格子,那么如果左插和上插都是 1,就应该将一个插头代表的路径反向,具体实现就是将上插对应的那个 2 的插头更改为 1,同时删除当前格子的左插和上插(这个不好用矩阵来画图表达,可以自己在草稿纸上画一画,草稿纸的功能可比这玩意多多了)。

5. 左插上插都为 2

与上面的那种情况也是类似的,需要将当前的两个插头删除,然后找到左插对应的 1 的插头并将其更改为 2

6. 左插为 2,上插为 1

当前格子的插头刚好是能让两条路径一进一出的接上,所以直接删除这两个插头就可以了(我把 2 的插头画成远离当前格子的方向会不会更好看):

1200

7. 左插为 1,上插为 2

既然这条路径都能向上走了,那么肯定就会构成回路了,所以此时更新答案:

2100

因为在写这篇代码的时候直接套用了模板题的思路和代码,所以使用了哈希来优化状态数,但是实际上 m6 的数据量完全是不需要的(所以我就把哈希的模写成了与数组长度相等)。需要注意的是上面分类讨论的时候的转移是需要进行一定的判断的(判断新的状态是否合法,比如插头是否插到方格图外面去了这种情况),因为使用了哈希,如果出现了非法情况,那么之后会再在这些非法情况上生成新的非法情况,会导致 RE 或者是 TLE(这两者的区别取决于你的数组开了多大,太大就会超时,太小就会运行时错误)。如果判断了非法情况的话,数组可以开的非常小(实测出来大概只有 200 左右的样子)。

Code

代码细节其实和模板题需要注意的几乎一致,所以就不再讲解了。

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
#define int long long
using namespace std;
void read(auto &k)
{
	k=0;auto flag=1;char b=getchar();
	while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
	while (isdigit(b)) {k=k*10+b-48;b=getchar();}
	k*=flag;
}
void write(auto k) {if (k<0) {putchar('-'),write(-k);return;}if (k>9) write(k/10);putchar(k%10+48);}
void writewith(auto k,char c) {write(k);putchar(c);}
int n,m,a[105][105],Fans=INT_MIN;
int pre=1,cur=0;
const int _SIZE=200,HSIZE=200;
int f[2][_SIZE+5],state[2][_SIZE+5],tot[2],bits[15];
struct HASH{
	int nxt,to;
}H[_SIZE+5];
int ptr[HSIZE+5],at;
void modify(int sta,int val)
{
	int key=sta%HSIZE;
	for (int i=ptr[key];i;i=H[i].nxt)
		if (state[cur][H[i].to]==sta)
		{
			f[cur][H[i].to]=max(f[cur][H[i].to],val);
			return;
		}
	tot[cur]++;if (tot[cur]>_SIZE) printf("?"),exit(0);//这一句类似于assert,只不过是想看测试点RE的原因
	state[cur][tot[cur]]=sta;
	f[cur][tot[cur]]=val;
	H[++at].nxt=ptr[key];
	H[at].to=tot[cur];
	ptr[key]=at;
}
void init(){for (int i=1;i<=10;i++) bits[i]=i<<1;}
void PlugDP()
{
	tot[cur]=1;
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=tot[cur];j++) state[cur][j]<<=2;
		for (int j=1;j<=m;j++)
		{
			swap(cur,pre);
			tot[cur]=0;at=0;mem(ptr,0);
			for (int k=1;k<=tot[pre];k++)
			{
				int nowans=f[pre][k],nowsta=state[pre][k];
				int isR=(nowsta>>bits[j-1])%4,isD=(nowsta>>bits[j])%4;
				if ((!isR) && (!isD))
				{
					if (j!=m && i!=n) modify(nowsta+(1<<bits[j-1])+2*(1<<bits[j]),nowans+a[i][j]);
					modify(nowsta,nowans);
				}
				else if ((!isR) && isD)
				{
					if (j!=m) modify(nowsta,nowans+a[i][j]);
					if (i!=n) modify(nowsta-isD*(1<<bits[j])+isD*(1<<bits[j-1]),nowans+a[i][j]);
				}
				else if (isR && (!isD))
				{
					if (i!=n) modify(nowsta,nowans+a[i][j]);
					if (j!=m) modify(nowsta-isR*(1<<bits[j-1])+isR*(1<<bits[j]),nowans+a[i][j]);
				}
				else if (isR==1 && isD==1)
				{
					int cnt=1;
					for (int l=j+1;l<=m;l++)
					{
						if ((nowsta>>bits[l])%4==1) cnt++;
						else if ((nowsta>>bits[l])%4==2) cnt--;
						if (!cnt)
						{
							modify(nowsta-(1<<bits[j-1])-(1<<bits[j])-(1<<bits[l]),nowans+a[i][j]);
							break;
						}
					}
				}
				else if (isR==2 && isD==2)
				{
					int cnt=1;
					for (int l=j-2;l;l--)
					{
						if ((nowsta>>bits[l])%4==1) cnt--;
						else if ((nowsta>>bits[l])%4==2) cnt++;
						if (!cnt)
						{
							modify(nowsta-2*(1<<bits[j-1])-2*(1<<bits[j])+(1<<bits[l]),nowans+a[i][j]);
							break;
						}
					}
				}
				else if (isR==2 && isD==1)
					modify(nowsta-(1<<bits[j])-2*(1<<bits[j-1]),nowans+a[i][j]);
				else if (isR==1 && isD==2 && 2*(1<<bits[j])+(1<<bits[j-1])==nowsta)
					Fans=max(Fans,nowans+a[i][j]);
			}
		}
	}
}
signed main()
{
	read(n),read(m);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++) read(a[i][j]);
	init();
	PlugDP();
	writewith(Fans,'\n');
	return 0;
}

posted @   Hanx16Msgr  阅读(42)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起