zoukankan      html  css  js  c++  java
  • Codeforces 1428D Bouncing Boomerangs

    (mathcal{Translate})

    题目链接

    在一个 (n imes n) 的网格图中,在下面的每一列都会射出一个飞镖,在 (n imes n) 的网格图中会有若干个目标,如果飞镖遇到目标则顺时针旋转 (90^{circ}) 后继续飞行直到飞出网格图。

    规定在每一行每一列最多有 (2) 个目标,第 (i) 行的飞镖会旋转 (a_i) 次,现给定 (a_i), 构造出一组合法的解(确定网格图中的目标的个数和位置)。

    (nleq 10^5,0leq a_ileq 3)

    凉心构造题。

    (mathcal{Solution})

    对于 (a_i=0),忽略这个地方,不会对答案产生什么影响。

    先处理 (a_i=1) 的情况,因为 (a_i=2,3) 会依靠另一列来进行第二、三次反弹,而 (a_i=1) 不需要考虑,直接出去占用一行就可以。怎样占用最优?因为其它 (3) 匹配这个 (1) 进行三次反弹的时候需要上面多开一行,所以从下往上依次占用。

    从左往右找 (a_i=2),之后再找到右边第一个没有被前面的 (2) 匹配的 (1),匹配到一起,也就是利用这个 (1) 的这一列目标进行第二次反弹。

    为什么找右边第一个 (1) ? 因为 (2) 需要右边的 (1) 来反弹,贪心一下是选尽量靠左的更优,把尽量靠右让给右边的 (2)

    为什么先找 (a_i=2)?因为 (a_i=3) 可以依靠其他的 (1,2,3) 进行 (3) 次反转。

    最后就是 (a_i=3) 的情况,它可以有三种方法来进行三次反弹:

    1. 右边没有被其它 (2,3) 匹配的 (1)

    2. 右边没有被其它 (3) 匹配的 (2)

    3. 右边没有被其它 (3) 匹配的 (3)

    因为可以从右边的 (3) 来反弹,所以这里 (3) 从后往前匹配找。

    (a_i=2,3) 去匹配的时候采用双指针扫,一个扫 (a_i=2,3),一个扫哪一位可以匹配。如果没扫到就是无解。

    (mathcal{Code})

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    #define re register
    //#define int long long
    inline int Max(int x, int y) { return x > y ? x : y; }
    inline int Min(int x, int y) { return x < y ? x : y; }
    inline int Abs(int x) { return x < 0 ? ~x + 1 : x; }
    inline int read() {
    	int r = 0;
    	bool w = 0;
    	char ch = getchar();
    	while(ch < '0' || ch > '9') {
    		if(ch == '-') w = 1;
    		ch = getchar();
    	}
    	while(ch >= '0' && ch <= '9') {
    		r = (r << 3) + (r << 1) + (ch ^ 48);
    		ch = getchar();
    	}
    	return w ? ~r + 1 : r;
    }
    //#undef int
    const int N = 300010;
    bool f;
    int n, a[N], cnt, ansx[N], ansy[N];
    bool vis[N];
    int row[N], rowcnt; 
    signed main() {
    	n = read();
    	for(int i = 1; i <= n; ++i) a[i] = read();
    	//先把所有的1一行一行开 
    	for(int i = 1; i <= n; ++i)
    		if(a[i] == 1) ++cnt, ansx[cnt] = i, ansy[cnt] = ++rowcnt, row[i] = rowcnt;
    	int pos = 1;
    	//2和右边第一个1匹配 
    	for(int i = 1; i <= n; ++i) {
    		if(a[i] == 2) {
    			pos = Max(pos, i);
    			while(a[pos] != 1 && pos <= n) ++pos;
    			if(pos == n + 1) {
    				puts("-1");
    				return 0;
    			}
    			++cnt;
    			ansx[cnt] = i, row[i] = ansy[cnt] = row[pos];
    			vis[pos] = 1;
    			++pos;
    		}
    	}
    	//3和右边三种情况匹配 
    	pos = n;
    	for(int i = n; i >= 1; --i) {
    		if(a[i] == 3) {
    			while(!((a[pos] == 1 && !vis[pos]) || a[pos] == 2 || (a[pos] == 3 && row[pos])) && pos >= i) --pos;
    			if(pos == i - 1) {
    				puts("-1");
    				return 0;
    			}
    			++cnt; ansx[cnt] = i, ansy[cnt] = ++rowcnt;
    			++cnt; ansx[cnt] = pos, ansy[cnt] = rowcnt;
    			row[i] = rowcnt;
    			--pos;
    		}
    	}
    	//无解 
    	if(cnt > n << 1 || rowcnt > n) {
    		puts("-1");
    		return 0;
    	}
    	printf("%d
    ", cnt);
    	for(int i = 1; i <= cnt; ++i) printf("%d %d
    ", n - ansy[i] + 1, ansx[i]);
    	return 0;
    }
    
  • 相关阅读:
    初级工程师该如何去学习,如何去研发开关电源?
    CentOS-pam认证机制简介
    Linux-密码复杂度限制
    如何写一个简单的webserver(一):最简实现
    MySQL5.6绿色版安装(mysql-5.6.24-winx64.zip)
    Linux忘记roo密码的解决办法
    Linux中的netstat命令详解
    WireShark抓包分析(二)
    Wireshark抓取TCP包分析
    Wireshark 基本介绍和学习 TCP 三次握手
  • 原文地址:https://www.cnblogs.com/do-while-true/p/13841797.html
Copyright © 2011-2022 走看看