2020暑期D3-D 思路+证明
主为自用,欢迎指正。
https://ac.nowcoder.com/acm/contest/5668/D
前置知识:
题意:
一无限大二维平面中,存在无穷的单位方格,所有方格初始为白色,现给n方格涂黑,要求存在m个点对。
点对符合:上下左右相邻的点为异色。
输入 n,m。
输出 存在则‘Yes’,且给出符合坐标;否则‘No’。
思路:
先判是否存在,不存在的情况同意输出‘No’。容易得到若有n个黑格,则点对数量m必然存在上下界,同时m必须为偶数(若存在黑格相邻,点对数量4n-2相邻次数)
if(m>4n||m%2),则为‘No’;
设黑格存在与不同a行,不同b列。则点对数量为2(a+b);
首先对黑格团进行模拟,得到黑格为n时最少的点对数,且记录黑格块位置。判断掉不可能的情况,在存在题意情况下遍历找出成团黑格的数量,并记录位置,后输出不成团的位置即可。
随后翻阅大佬题解发现一种思路清晰,代码明了的思路。特在此说明。(参考自http://www.koule2333.top:3000/s/r13vgvelP)
首先考虑所有黑格皆排成一排,则mm=2*n+2(除第一个点提供四个点对),若m>mm,则每取走一个末尾黑格,对点数+2;若m<mm,则将原一排黑格尽量均分长高相等的‘L’形,随后从末尾取黑格放置最小行,最小列处,点对数-2.
代码步骤:
AC代码:
第一种思路:
#include <stdio.h> #include <algorithm> #include <iostream> using namespace std; typedef long long ll; const ll maxn = 2e5 + 111; ll num[100]; ll mp[1000][1000] = {0}; pair<ll, ll> pp[100]; int main() { num[1] = 4; pp[1] = {1, 1}; for (ll i = 2; i <= 50; ++i) { //黑格块模拟 ll j; for (j = 1; j <= i; ++j) { if (j * j >= i) { break; } } num[i] = (j + (i + j - 1) / j) * 2; //得黑格块点对 pp[i] = {j, (i + j - 1) / j}; //确定黑格块相异行列值 } ll T; scanf("%lld", &T); while (T--) { ll n, m; scanf("%lld%lld", &n, &m); for (int i = 1; i <= 100; ++i) { for (int j = 1; j <= 100; ++j) { mp[i][j] = 0; } } if ((m & 1) || (m > 4 * n)) { puts("No"); } else { ll flag = 0; ll depe = 0; ll trans = 0; ll nnn, mmm; for (ll i = 1; i <= n; ++i) { // 黑格团点数 ll num_d = i; ll minn = num[i] + 4 * (n - i), maxx = 2 * i + 2 + 4 * (n - i); //确定m的上下界 if (m <= maxx && m >= minn) { flag = 1; depe = n - i; trans = (m - minn) / 2; //实际相邻黑格数 nnn = pp[i].first, mmm = pp[i].second; for (int ii = pp[i].first; ii >= 1; --ii) { for (int jj = pp[i].second; jj >= 1; --jj) { //位置递减遍历,黑格团位置被登记。 mp[ii][jj] = 1; --num_d; if (!num_d) break; } if (!num_d) break; } break; } } if (!flag) { puts("No"); } else { puts("Yes"); for (int i = 1, num = 1; num <= depe; ++num, i += 5) { printf("%d %d\n", i, 10000000); } int ni = 1, nj = 1; while (mp[ni][nj] == 0) { ++nj; if (nj == mmm) { nj = 1; ++ni; } } int toi = nnn, toj = mmm + 1; while (trans--) { mp[ni][nj] = 0; ++nj; if (nj == mmm) { nj = 1; ++ni; } mp[toi][toj++] = 1; } for (int i = 1; i <= 100; ++i) { for (int j = 1; j <= 100; ++j) { if (mp[i][j]) { printf("%d %d\n", i, j); } } } } } } return 0; }
第二种思路:
#include <bits/stdc++.h> using namespace std; const int N = 505; struct Point{ int x,y; }p[N]; int n,m,tot,t; bool solve(){ tot=0; if(m&1) return false; if(n*4<m) return false; if(n==1){ if(m==4){ p[++tot].x=1; p[tot].y=1; return true; } else return false; } if(m<4) return false; if(n*4==m){ for(int i=1;i<=n;++i) p[i].x=i*2+12345,p[i].y=i*2+12345; tot=n; return true; } int x=0,y=0,z=0; x=m/2-1; if(x>n){ for(int i=1;i<=x-n;++i){ p[i].x=i*2+12345,p[i].y=i*2+12345; } tot=x-n; x-=x-n; if(x<0) return false; for(int i=tot+1;i<=n;++i){ p[i].x=1; p[i].y=i; } tot=n; return true; }else{ z=x; x=(z+1)/2; y=(z+1)-x; if(x*y<n) return false; for(int i=1;i<=x;++i){ ++tot; p[tot].x=i; p[tot].y=1; } for(int i=2;i<=y;++i){ ++tot; p[tot].y=i; p[tot].x=1; } for(int i=2;i<=x;++i){ for(int j=2;j<=y;++j){ if(tot==n) break; tot++; p[tot].x=i; p[tot].y=j; } if(tot==n) break; } return true; } } int main(){ scanf("%d",&t); while(t--){ scanf("%d %d",&n,&m); if(!solve()) printf("No\n"); else{ printf("Yes\n"); for(int i=1;i<=tot;++i) printf("%d %d\n",p[i].x,p[i].y); } } }
简单图证:
注:
代码还将另作注释。