zoukankan      html  css  js  c++  java
  • 【题解】[HNOI2004] 打鼹鼠 By 5ab as a juruo.

    题目信息

    题目来源:CCF 海南省选 2004;

    在线评测地址:Luogu#2285BZOJ1207

    运行限制:时间不超过 (1.00 extrm{s}),空间不超过 (128 extrm{MiB})。(比赛中要求从 input.txt 中读入,输出到 output.txt 中)

    题目描述

    鼹鼠是一种很喜欢挖洞的动物,但每过一定的时间,它还是喜欢把头探出到地面上来透透气的。根据这个特点阿牛编写了一个打鼹鼠的游戏:

    在一个 (n imes n) 的网格中,在某些时刻鼹鼠会在某一个网格探出头来透透气。你可以控制一个机器人来打鼹鼠,如果 (i) 时刻鼹鼠在某个网格中出现,而机器人也处于同一网格的话,那么这个鼹鼠就会被机器人打死。而机器人每一时刻只能够移动一格或停留在原地不动。机器人的移动是指从当前所处的网格移向相邻的网格,即从坐标为 ((i,j)) 的网格移向 ((i-1,j))((i+1,j))((i,j-1))((i,j+1)) 四个网格,机器人不能走出整个 (n imes n) 的网格。游戏开始时,你可以自由选定机器人的初始位置。

    现在知道在一段时间内,鼹鼠出现的时间和地点,请编写一个程序使机器人在这一段时间内打死尽可能多的鼹鼠。

    输入格式

    第一行为 (n)(m),其中 (m) 表示在这一段时间内出现的鼹鼠的个数。

    接下来的 (m) 行中每行有三个数据 (time)(x)(y) 表示有一只鼹鼠在游戏开始后第 (time) 个时刻,在第 (x) 行第 (y) 个网格里出现了一只鼹鼠。(time) 按递增的顺序给出。

    同一时刻可能出现多只鼹鼠,但同一时刻同一地点只可能出现一只鼹鼠。

    输出格式

    仅包含一个正整数,表示被打死鼹鼠的最大数目。

    数据规模与约定

    (1le nle 1000)(1le mle 10000)

    保证 (1le x,yle n)

    分析

    题目大意是说,给你 (m) 个点,每个点在固定的时间出现并立即消失。你可以控制一个动点,让它与尽可能多的点重合,求出最大的重合数。

    我们注意到,这个动点的移动可以分解成从一个点另一个点。这样我们就可以 DP。

    定义 (f_i) 为与第 (i) 个点重合时的最大重合数。显然应该从一个 (f_j) 转移而来。

    考虑 (f_j) 的合法条件,如果两个点之间的曼哈顿距离({}^{[1]})小于等于这两点出现时间差,那么这个动点就能从 (j) 转移到 (i)

    显然,(f_i) 就是对于所有合法的 (f_j) 求最大值,然后加上 (1)。由于这道题难判是否合法,所以复杂度只有 (mathcal{O}(m^2))。由于还有一个 (dfrac{1}{2}) 的常数,所以可以勉强卡过去。

    注意事项

    1. 因为难判是否合法,所以 (f) 是不单调的,别忘了所有数取 max;
    2. 由于可以从任何点开始,所以 (f_i) 初始为 (1)

    Code

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int max_n = 10000;
    struct rat
    {
    	int t, x, y;
    
    	bool operator<(const rat& rt) const
    	{
    		return t < rt.t;
    	}
    };
    
    rat rts[max_n]; // 存信息
    int dp[max_n]; // DP 数组
    
    inline int my_abs(int x) { return (x < 0)? -x:x; } // 绝对值
    
    int main()
    {
    	int n, m, ans = 0;
    
    	scanf("%d%d", &n, &m); // 输入
    	for (int i = 0; i < m; i++)
    		scanf("%d%d%d", &rts[i].t, &rts[i].x, &rts[i].y);
    	
    	sort(rts, rts + m); // 排序(虽然题意中已经说递增了)
    
    	for (int i = 0; i < m; i++) // 可以在任何地方开始
    		dp[i] = 1;
    	
    	for (int i = 1; i < m; i++) // DP
    		for (int j = 0; j < i; j++)
    			if (my_abs(rts[i].x - rts[j].x) + my_abs(rts[i].y - rts[j].y) <= rts[i].t - rts[j].t && dp[i] < dp[j] + 1) // 合法且更优
    				dp[i] = dp[j] + 1; // 更新
    
    	for (int i = 0; i < m; i++) // 取答案
    		if (dp[i] > ans)
    			ans = dp[i];
    	
    	printf("%d
    ", ans); // 输出
    
    	return 0; // 然后就 AC 了、
    }
    

    References

    1. 两个点 (p_0=(x_0,y_0))(p_1=(x_1,y_1)) 之间的曼哈顿距离 (d(p_0,p_1))(|x_0-x_1|+|y_0-y_1|)
  • 相关阅读:
    win10上使用linux命令
    leetcode--js--Median of Two Sorted Arrays
    leetcode--js--Longest Substring Without Repeating Characters
    Linux常用的命令
    微信小程序
    leetcode—js—Add Two Numbers
    PHPExcel使用
    console控制台的用法
    git中常混淆的操作
    mysql解析json下的某个字段
  • 原文地址:https://www.cnblogs.com/5ab-juruo/p/solution-hnoi2004_lg2285_bzoj1207.html
Copyright © 2011-2022 走看看