网格贪吃蛇
- 题目浏览
- 算法浏览
- 核心思路
- ?2022-4-2日新增内容
-
- c 代码
题目浏览
时间限制:1.0s 内存限制:256.0MB
曾经风靡全球的贪吃蛇游戏又回来了!这一次,贪吃蛇在m行n网格沿网格线爬行,从左下角坐标为(0,0)的网格点出发,只能在每个网格点向上或向右爬行,爬到右上角的坐标为(m-1,n-1)格点结束游戏。网格上指定的格点有贪吃蛇喜欢吃的豆豆。给出网格信息。请计算贪吃蛇最多能吃多少豆豆。 输入数据的第一行为是两个整数m、n(用空格隔开)分别代表网格的行数和列数;第二种行为是整数k,代表网格上豆豆的数量;第三行到第k 2行是k豆的横纵坐标x、y(用空间隔开)。 为了最大限度地吃豆豆,程序输出一行。
10 10 10 3 0 1 5 4 0 2 5 3 4 6 5 8 6 2 6 6 7 3 1
5
1 ≤ m , n ≤ 1 0 6 , 0 ≤ x ≤ m ? 1 , 0 ≤ y ≤ n ? 1 , 1 ≤ k ≤ 1000 1≤m, n≤10^6,0≤x≤m-1,0≤y≤n-1,1≤k≤1000 1≤m,n≤106,0≤x≤m?1,0≤y≤n?1,1≤k≤1000
算法浏览
(这边建议看新写的代码, 这个代码太拉了)
#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
#include<set>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010;
int n, m, k;
int g[N][N];
int dp[N][N];
vector<PII> node;
map<int, int> dx;//x对应的坐标
map<int, int> dy;//y对应的坐标
int main()
{
cin >> m >> n >> k;
for(int i = 0; i < k; i++)//读入每个点
{
int x, y;
scanf("%d%d", &x, &y);
PII temp(x,y);
node.push_back(temp);
dx[x] = 0;
dy[y] = 0;
}
int nx,ny;//映射以后的长度和宽度
int j = 1;
for(map<int, int> :: iterator i = dx.begin(); i != dx.end(); i++, j++) i->second = j;//设置每个值对应的映射值 x坐标
nx = j - 1;
j = 1;
for(map<int, int> :: iterator i = dy.begin(); i != dy.end(); i++, j++) i->second = j;//设置每个值对应的映射值 y坐标
ny = j - 1;
for(int i = 0; i < node.size(); i++)//将映射后的坐标在图上标注出来
{
PII temp = node[i];
g[dx[temp.x]][dy[temp.y]] = 1;
}
//动态规划
for(int i = 1; i <= nx; i++){
for(int j = 1; j <= ny; j++)
{
dp[i][j] = g[i][j];
dp[i][j] += max(dp[i - 1][j], dp[i][j - 1]);
}
}
cout << dp[nx][ny] << endl;
return 0;
}
核心思路
本题考查的是
首先先看一下这个数据范围,可以知道,地图很大,但是点数很稀疏,这个情况怎么办? 若将每个点直接在地图上标注出来以后会浪费大量的时间在计算没有意义的点(实际上也根本开不出来这么大的地图)
所以可以将根本没有用到的行和列剔除掉,换一种思路,即分别计算x轴和y轴各个点之间的相对的位置 。 举个例子(2, 5), (1000, 633), (1000,800), (2560,48520)可以映射成(1,1), (2,2), (2,3), (3, 4)这样就可以将一个很大的地图变成一个很小的地图,再在这个很小的地图上做动态规划可以通过用例。
✨2022-4-2日新增的内容✨
这几天学了一下离散化,然后想到这道题可以使用离散化的思想来写出来, 其实我之前的代码也是用的是离散化的思想来写出来的,但是那个东绕西绕的很容易想的混乱,所以今天借此机会,又重新写了一遍代码,另外加上了注释
同时,这个是我整理出来离散化的笔记 : 链接
c++代码
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef pair<int, int> PII;
const int N = 2010; // 要离散化每个点的坐标, 而总共会有1000个点, 每个点2个数字,所以地图要开2000的大小
int n, m ,k;
int g[N][N]; // 离散化后的地图
int dp[N][N]; // 背包问题
vector<int> alls; // 离散化的点的位置
vector<PII> loc; // 待离散化的点的坐标
int find(int x) {
// 查找点x离散化后的数
int l = 0, r = alls.size(); // 二分法
while (l < r) {
int mid = (l + r) >> 1;
if (alls[mid] >= x) r = mid;
else l = mid + 1;
}
return l;
}
int main() {
cin >> n >> m; // 先输入n行, m列
cin >> k;
while (k --) {
int x, y;
cin >> x >> y;
loc.push_back({
x, y}); // 将该点输入到待离散化的vector中
alls.push_back(x); // 将数字添加到待离散化的数字中
alls.push_back(y);
}
// 离散化 + 去重
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
for (int i = 0, len = loc.size(); i < len; i++) {
// 将每个坐标离散化后的坐标添加到地图中
PII item = loc[i];
g[find(item.first) + 1][find(item.second) + 1] = 1;
}
/* 这个地方这样写也可以,但是蓝桥杯的练习系统不支持c++11,所以就只能用上面的那种写法了 for (auto item : loc) { g[find(item.first) + 1][find(item.second) + 1] = 1; } */
int len = alls.size(); // 01背包问题
for (int i = 1; i <= len; i++)
for (int j = 1; j <= len; j++) {
dp[i][j] = g[i][j];
dp[i][j] += max(dp[i - 1][j], dp[i][j - 1]);
}
cout << dp[len][len] << endl; // 输出答案
return 0;
}
本次的代码和上次的代码在离散化的地方有些不同, 之前的代码是离散化x轴和y轴的坐标, 而这次的代码则是离散化x轴 + y轴的坐标