麦当劳叔叔的难题

题目描述

话说我们铭铭小朋友成功的回答了爸爸的问题,自然少不了要去索要些奖励,抠门的爸爸一看报纸,嘿,门口的麦当劳在搞活动,还有免费午餐哦,不过前提条件:得正确回答麦当劳叔叔的问题。

问题是这样描述的:

“我面前有很多个小朋友,我希望你帮我找到一个最聪明的小朋友。我心目中最聪明的就是第一个跑进麦当劳大门的,我希望你帮我找出最聪明和最不聪明的小朋友,可能的最大的到达时间差。但是,小朋友只能按照一个特殊的规则前进。小朋友面前有一个 n\times nn×n 的格子矩阵,左下角的格子是起点,右上角的格子是大门。每个孩子每秒可以走向 上、下、左、右 前进一个格子,每个格子只能经过一次。但矩阵中间有一些障碍区,不能通过,只能绕过。”

例如,4\times 44×4 的矩阵,格子 (1, 1),(2, 3),(4, 2)(1,1),(2,3),(4,2) 为障碍区,黑格子就是一条可行的路线。时间为 77。

输入格式

第一行为两个整数 n, mn,m。

第二至第 m+1m+1 行里,每行描述一个障碍区。用两个整数表示 x, yx,y。

输出格式

仅一行,那个最大的时间差。

输入输出样例

输入 #1复制

4 3
1 1
2 3
4 2

输出 #1复制

4

题解

题意简述:给一个长和宽都为 nn 的方阵,方阵上有的点为 00,有的点为 11,为 00 的点不能走,要求出从左下角走到右上角的最长路线和最短路线的差值。

分析

本题解默认所有读者都已经会插头 DP 思想以及其模板,如果不会请先学习再来阅读!模板指路

最短路很好求,无论是 BFS 还是 DFS 都能在正确的时间复杂度内跑出结果,所以我们主要要求最长路。看到 n\le10n≤10 的时候第一反应是搜索,但是看了一眼讨论区,搜索的复杂度是假的,结合这么小的数据,比较能想到的就是状压 DP,然后便能比较自然的想到是插头 DP 了。

思考怎么进行插头 DP,显然,这道题要求的是一条路径,而不是一条回路。那我们可以尝试将问题转换成我们熟悉的范围内:求回路。(不确定求路径能不能做,因为我尝试过写求路径然后成功的写挂了)。

有一个十分巧妙的转换,不过这种转化用的也比较多,就是在原方阵外面再围一圈作为哨兵。(这种思想在很多搜索题里面都存在,来避免一些边界问题,当然,这道题用到的思想不太一样,只是类似而已)。


如图所示(样例):其中 11 代表能走的。

注意,因为题目让我们求左下角到右上角,但是这样我们就要倒着遍历,很麻烦,那我们把整个方阵做一个竖直翻转,变为从左上角走到右下角即可。

我们发现,原图被我们规定在了右上角,而到从有哨兵的图到原图只有两个地方能过去,也就是原图起点的左边和原图终点的下面,这样围成的回路我们能保证一定经过原图的起点和终点,也就是我们的路径中有一段全是哨兵,而另一段就是最长路。求最长路只需减掉哨兵就好。

那么我们发现现在其实就是插头 DP 的板子了,板子指路,不过我们把求方案数改成了求路径长度。也很简单,我们不存方案数存路径长度,转移时取最大的而不是相加即可。

代码

代码不难,不过有有一些小技巧,比如说初始化时把长宽都为 n+2n+2 的方阵初始化为 11,这样在进行插头 DP 的时候,不是 11 的要么是越界要么是障碍物,省去了判断边界的步骤。

#include <bits/stdc++.h>
using namespace std;

const int N = 11810 , M = 23007;

int n , m;
int g[20][20];
int h[2][M] , q[2][N] , cnt[2];
int v[2][M];
bool vis[20][20];
int dx[] = {0 , 1 , -1 , 0} , dy[] = {1 , 0 , 0 , -1};
struct node 
{
	int x , y , step;
};

inline int find (int cur , int x)//find和insert都是哈希表用的函数
{
	int t = x%M;
	while (h[cur][t]!=-1 && h[cur][t]!=x)
	{
		if (++t==M) t = 0;
	}
	return t;
}

inline void insert (int cur , int state , int w)
{
	int t = find (cur , state);
	if (h[cur][t]==-1)
	{
		h[cur][t] = state , v[cur][t] = w;
		q[cur][++cnt[cur]] = t;
	}
	else v[cur][t] = max(w , v[cur][t]);
}

inline int get (int state , int k)//返回第k位的值
{
	return (state>>(k*2))&3;
}

inline int build (int k , int x)//返回第k位为x在10进制下的值
{
	return x*(1<<(k*2));
}

void print(int state)//调试不用管,不过这个调试很好用,分享一下
{
	int temp=n+1;
	while(temp)
	{
		cout<<(state&3);
		state>>=2;
		--temp;
	}
}

inline int get_max ()//插头DP求最长路
{
	int res = -1e8;
	int cur = 0;
	memset (h , -1 , sizeof h);
	insert (cur , 0 , 1);
	for (int i=1 ; i<=n ; i++)
	{
		for (int j=1 ; j<=cnt[cur] ; j++)//换行
		{
			h[cur][q[cur][j]] <<= 2;
		}
		for (int j=1 ; j<=n ; j++)
		{
//			cout << "i:" << i << " j:" << j << '\n';
			int last = cur;
			cur ^= 1 , cnt[cur] = 0;
			memset (h[cur] , -1 , sizeof h[cur]);
			for (int k=1 ; k<=cnt[last] ; k++)
			{
				int state = h[last][q[last][k]] , w = v[last][q[last][k]];
				int x = get (state , j-1) , y = get (state , j);
//				print(state);
//				cout << ' ' << w << ' ' << x << ' ' << y << '\n';
				if (!g[i][j])
				{ 
					insert (cur , state , w);
				}
				else if (!x && !y)
				{
					insert (cur , state , w);
					if (g[i+1][j] && g[i][j+1]) 
					{
						insert (cur , state+build(j-1 , 1)+build(j , 2) , w+1);
					}
				}
				else if (!x && y)
				{
					if (g[i][j+1]) insert (cur , state , w+1);
					if (g[i+1][j]) insert (cur , state-build(j , y)+build(j-1 , y) , w+1);
				}
				else if (x && !y)
				{
					if (g[i+1][j]) insert (cur , state , w+1);
					if (g[i][j+1]) insert (cur , state-build(j-1 , x)+build(j , x) , w+1);
				}
				else if (x==1 && y==1)
				{
					for (int u=j+1 , s=1 ; ; u++)
					{
						int z = get (state , u);
						if (z==1) s++;
						else if (z==2)
						{
							if (--s==0)
							{
								insert (cur , state-build(j , y)-build(j-1 , x)-build(u , 1) , w+1);
								break;
							}
						}
					}
				}
				else if (x==2 && y==2)
				{
					for (int u=j-2 , s=1 ; ; u--)
						{
						int z = get (state , u);
						if (z==2) s++;
						else if (z==1)
						{
							if (--s==0)
							{
								insert (cur , state-build(j , y)-build(j-1 , x)+build(u , 1) , w+1);
								break;
							}
						}
					}
				}
				else if (x==2 && y==1)
				{
					insert (cur , state-build(j-1 , x)-build(j , y) , w+1);
				}
				else if (i==n && j==n)
				{ 
					res = max(res , w);
				}
			}
		}
	}
	return res-2*n-1;
}

inline int get_min ()//最短路很好写
{
	queue <node> q;
	q.push ({1 , 3 , 1});
	while (!q.empty())
	{
		int x = q.front().x , y = q.front().y , step = q.front().step;
		q.pop();
		if (x==n-2 && y==n) 
		{
//			cout << step << '\n';
			return step;
		}
		vis[x][y] = 1;
		for (int i=0 ; i<4 ; i++)
		{
			int xx = dx[i]+x , yy = dy[i]+y;
			if (xx>=1 && xx<=n-2 && yy>=3 && yy<=n && g[xx][yy] && !vis[xx][yy])
			{
				q.push({xx , yy , step+1});
			}
		}
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);
	
	cin >> n >> m;
	n += 2;
	for (int i=1 ; i<=n ; i++)//全部初始化为1,避免某些边界问题
	{
		for (int j=1 ; j<=n ; j++)
		{
			g[i][j] = 1;
		}
	}
	for (int i=2 ; i<=n-1 ; i++)//添加不能走的哨兵
	{
		g[i][2] = 0 , g[2][i] = 0;
	}
	for (int i=1 ; i<=m ; i++)
	{
		int a , b;
		cin >> a >> b;
		g[a+2][b+2] = 0;
	}
	
	for (int i=1 ; i<=n/2 ; i++)//翻转矩阵
	{
		int tmp[20];
		memcpy (tmp , g[i] , sizeof g[i]);
		memcpy (g[i] , g[n-i+1] , sizeof g[i]);
		memcpy (g[n-i+1] , tmp , sizeof tmp);
	}
	
//	for (int i=1 ; i<=n ; i++)
//	{
//		for (int j=1 ; j<=n ; j++)
//		{
//			cout << g[i][j] << ' ';
//		}
//		cout << '\n';
//	}
	
	int res_max = get_max () , res_min = get_min ();
	cout << res_max-res_min << '\n';
	return 0;
}

麦当劳的HTML设计通常用于创建麦当劳官方网站或在线菜单页面。HTML (Hypertext Markup Language) 是一种用于构建网页结构的语言,它定义了文本、图像、链接等元素的布局和显示方式。以下是创建一个基本的麦当劳HTML页面的简要概述: 1. **文档结构**:开始一个HTML文件,通常包含`<!DOCTYPE html>`声明,表示这是HTML5文档,然后是`<html>`标签,里面包含`<head>`和`<body>`部分。 ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>麦当劳官网</title> <!-- CSS样式表链接 --> </head> <body> <!-- 页面内容 --> </body> </html> ``` 2. **头部信息**:在`<head>`中添加元数据,如标题`<title>`、CSS链接和可能的`<meta>`标签(如描述、关键词)。 3. **导航栏和页眉**:使用`<header>`标签创建页眉,其中可能包括品牌的标志和菜单链接 `<nav>`元素。 ```html <header> <a href="#home">麦当劳</a> <nav> <ul> <li><a href="#menu">菜单</a></li> <li><a href="#locations">门店</a></li> <li><a href="#contact">联系我们</a></li> </ul> </nav> </header> ``` 4. **主要内容**:在`<body>`中,可以使用`<section>`标签组织页面内容,如特色产品介绍、图片和价格等。 ```html <section id="home"> <h1>欢迎来到麦当劳</h1> <img src="mcdonalds-logo.jpg" alt="麦当劳Logo"> </section> <section id="menu"> <!-- 麦当劳菜单项 --> </section> ``` 5. **联系信息和底部**:添加`<footer>`标签,包含联系方式和其他必要信息。 相关问题-- 1. HTML中的`<header>`、`<nav>`和`<footer>`分别代表什么? 2. 如何在HTML中插入图片? 3. 如何使用CSS增强麦当劳HTML页面的视觉效果?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值