资源限制
时间限制: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
#include
#include
#include
#include
#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)这样就可以将一个很大的地图变成一个很小的地图,再在这个很小的地图上做动态规划可以通过用例。
这几天学了一下离散化,然后想到这道题可以使用离散化的思想来写出来, 其实我之前的代码也是用的是离散化的思想来写出来的,但是那个东绕西绕的很容易想的混乱,所以今天借此机会,又重新写了一遍代码,另外加上了注释
同时,这个是我整理出来离散化的笔记 : 链接
#include
#include
#include
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轴的坐标