zoukankan      html  css  js  c++  java
  • @noi


    @description@

    在一块平原上有一头大象。

    平原被分成 n×m 个格子。初始时大象位于 (1,1)。每一秒,大象会移动到一个相邻的格子上(四连通),但不会移动到平原外面。由于你视力不好,你无法知道大象每次移动到哪个格子上。

    你可以使用火球术来攻击地面。每次释放火球术,你可以攻击任意多个格子。每个格子只能被攻击一次。大象不能移动到被攻击过的格子上,如果大象相邻的格子都无法移动,那么它会停在原地不动。

    你的目标是最后只剩下一个格子没有被火球术攻击过,然后你去那里活捉大象。在此之前,你必须保证你的火球术不会击中大象。由于大象的战斗力十分强大,只有在某些格子中你才能抓住大象。

    然而,由于你魔法水平不高,在使用火球术前你需要进行蓄势。第一次蓄势的时间至少为 n+m 秒,接下来每次蓄势时间都要比上次长。蓄势时间必须是整数秒。你希望最小化最后一次的蓄势时间。

    注意:每一秒大象先移动,接着你才会释放火球术。也就是说,如果你第一次的蓄势时间为 n+m 秒,那么大象已经移动了 n+m 次。

    input
    第一行包含两个整数 n, m。n, m ≤ 1000。

    接下来 n 行每行 m 个数,每个数只能是 0 或 1。第 i+1 行第 j 列的数表示在 (i,j) 这个格子中,你能否抓住大象(1 表示能,0 表示不能)。

    output
    输出若干行,每行表示释放一次火球术。

    每行第一个整数表示蓄势时间,接下来若干个整数表示攻击的格子,第 i 行第 j 列的格子编号为 (i−1)∗m + j。每行最后输出一个 0。

    如果无解输出一行一个整数 −1。

    sample input
    3 3
    1 1 1
    1 1 1
    1 1 1
    sample output
    7 1 3 7 9 0
    9 2 4 6 8 0

    @solution@

    大象太强了。

    将矩形黑白染色,标记 (1, 1) 为白,则某时刻大象要么在黑格要么在白格。
    不妨假设大象在白格,则只能删除黑格,且要保证删完过后剩下的白格仍然连通才合法。删剩下最后一个格子则结束。
    我们可以将这个删格子的过程倒过来,从某一个格子开始往外添加同色格子,并且保持连通。于是就可以得到每一时刻我们需要删去格子的编号。

    如果我们蓄势时间为偶数,则大象的位置黑白颜色不变;否则位置变为相反的颜色。
    通过我们所知道的第一次删去格子的颜色,以及大象的初始位置在 (1, 1) 即白格,可以确定第一次是奇数蓄势还是偶数蓄势。
    假如经过一定次数的删格子后,大象在某种颜色的格子。则可以得到上一次一定是删异色的格子,则这一次应该删与大象所在格同色的格子,需要大象离开。
    所以得到:除了第一次蓄势时间奇偶性需要判断一下,剩下的蓄势时间一定为奇数。

    可以发现对于某一个点而言,第一次删去的肯定包括矩形四个角落的某一个。于是我们可以对于这个点到四个角落的曼哈顿距离求最大值,并判断第一次的奇偶性与 (m+n) 的奇偶性是否相同,就可以推出以这个点为终末状态的答案是多少。
    将所有的合法点都求解对应的答案,并取最小值。输出方案不难,按照加格子的思路来思考即可,或者也可以按照每一个点到终点的曼哈顿距离来思考。

    复杂度 O(nm)。

    @accepted code@

    #include<cstdio>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int MAXN = 1000;
    int n, m, x, y, cnt = 0;
    int fun(int x, int y) {
    	return max(max(x-1 + y-1, n-x + m-y), max(x-1 + m-y, n-x + y-1));
    }
    int abs(int x) {return x > 0 ? x : -x;}
    vector<int>ans[2*MAXN + 5];
    int X[MAXN*MAXN + 5], Y[MAXN*MAXN + 5];
    int main() {
    	scanf("%d%d", &n, &m), x = -1, y = -1;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++) {
    			int a; scanf("%d", &a);
    			if( a ) {
    				if( x == -1 && y == -1 )
    					x = i, y = j;
    				else if( fun(x, y) > fun(i, j) )
    					x = i, y = j;
    				else if( fun(x, y) == fun(i, j) ) {
    					if( ( (((x + y)&1) ^ (fun(x, y)&1)) == ((n + m)&1) ) && ( (((i + j)&1) ^ (fun(i, j)&1)) != ((n + m)&1) ) )
    						x = i, y = j;
    				}
    			}
    		}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			ans[abs(i-x)+abs(j-y)].push_back(++cnt), X[cnt] = i, Y[cnt] = j;
    	int p = n + m; bool flag = true;
    	for(int i=n+m;i>=1;i--) {
    		if( ans[i].empty() ) continue;
    		if( flag ) {
    			p += ((X[ans[i][0]]-1+Y[ans[i][0]]-1)&1)^((n+m)&1)^1, flag = false;
    			printf("%d", p);
    			if( p % 2 == 0 ) p--;
    		}
    		else {
    			p += 2;
    			printf("%d", p);
    		}
    		for(int j=0;j<ans[i].size();j++)
    			printf(" %d", ans[i][j]);
    		printf(" %d
    ", 0);
    	}
    }
    

    @details@

    啊啊虽然题解不难理解,代码也不是很难写,但是通过反过来加格子的角度来思考问题这个真的想不到啊。。。

    考场上只写了个 50 分的构造方法。。。

  • 相关阅读:
    WIN10解决:失败 – 检测到病毒文件下载失败问题
    Jinja2学习
    div设置百分比高度 宽度
    flask静态html
    python排序之冒泡排序
    python中的break continue之用法
    python中的break continue用法
    python格式化输出(% format用法)
    python基础数据类型
    linux下anaconda使用教程
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11115295.html
Copyright © 2011-2022 走看看