Crosses Puzzles zoj 4018 (zju校赛)
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5746
题目大意:
N*M的方格里,每个格子有一个指针,一开始指向上下左右四个方向中的一个,选一个格子点一次,那个格子的指针会顺时针转一下,接着被它指着的那个格子的针也会顺时针转一下,一直连锁下去。 构造一种不超过6000次点击的方案,使得所有针朝上。
题解:
$need[x][y]$表示这个格子的针转多少次可以朝上。
$A[x][y]$表示这个格子主动转了多少次。
$B[x][y]$表示这个格子由于受到边上格子的影响被动转了多少次。
那么有:
$A[x][y] + B[x][y] = 4 * K[x][y] + need[x][y]$. (等式1)
$B[x][y] = \sum (K[x'][y'] + [(x', y')转到朝上的过程中会影响到(x, y)]) $ (等式2)
其中$(x', y')是和(x, y)相邻的格子$
一开始先假设所有的 $K[x][y] = 0$,那么所有的$A[x][y]$ $B[x][y]$ 都可以计算出来。但是某些$A[x][y]$会是负数。
考虑把一些$K[x][y]$加大。 如果把$K[x][y] += 1$, 为了使得之前的等式仍然成立,必须有$A[x][y] += 4$,所有和它相邻的格子(x', y')必须 $A[x'][y'] -= 1, B[x'][y'] += 1$。
然后先本地开始乱shi:
因为不超过6000步,平均每个格子60步。
所以从上到下从左到右,如果发现当前格子的$A[x][y] <= 56$, 就不断让当前格子+4,和它相邻格子-1。
这样做一次后,发现最外面的一圈的$A[x][y]$都是几十了。中间的还是会有一些负数。
重复上面的过程,发现每做一次都会使得A为几十的圈子往里缩小。重复10次就ok了。
思考:为什么所有让$A[x][y]>=0$ 就好了呢?
因为一组合法的$A[x][y]$,可以解出所有的$B[x][y]$ 和 $K[x][y]$.且解是唯一的。
证明: 把等式1中的B用K表示带入等式二,得到$N^2$个关于$K$的方程,$K$有$N^2$个变量,所以如果有解,解一定唯一。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define MAXN 15 5 6 int n, m; 7 int a[MAXN][MAXN], x[MAXN][MAXN], y[MAXN][MAXN]; 8 int dx[] = {-1, 0, 1, 0}; 9 int dy[] = {0, 1, 0, -1}; 10 int ok[4][4], need[MAXN][MAXN]; 11 12 bool check(int x, int y) 13 { 14 return x >= 1 && x <= n && y <= m && y >= 1 && a[x][y] != -1; 15 } 16 17 int main() 18 { 19 //freopen("in.txt", "r", stdin); 20 ok[0][1] = 1; 21 ok[1][1] = ok[1][2] = 1; 22 ok[2][1] = ok[2][2] = ok[2][3] = 1; 23 24 int T; 25 scanf("%d", &T); 26 while (T--) 27 { 28 scanf("%d %d", &n, &m); 29 for (int i = 1; i <= n; ++i) 30 { 31 for (int j = 1; j <= m; ++j) 32 { 33 scanf("%d", &a[i][j]); 34 if (a[i][j] == -1) continue; 35 a[i][j] = (4 - a[i][j]) % 4; 36 need[i][j] = (4 - a[i][j]) % 4; 37 } 38 } 39 memset(x, 0, sizeof(x)); 40 memset(y, 0, sizeof(y)); 41 int _i, _j; 42 for (int i = 1; i <= n; ++i) 43 { 44 for (int j = 1; j <= m; ++j) 45 { 46 if (a[i][j] == -1) continue; 47 for (int d = 0; d < 4; ++d) 48 { 49 _i = i + dx[d], _j = j + dy[d]; 50 if (check(_i, _j) && ok[d][a[_i][_j]]) 51 ++y[i][j]; 52 } 53 x[i][j] = need[i][j] - y[i][j]; 54 } 55 } 56 int iters = 10; 57 while (iters--) 58 { 59 for (int i = 1; i <= n; ++i) 60 { 61 for (int j = 1; j <= m; ++j) 62 { 63 if (a[i][j] == -1) continue; 64 while (x[i][j] + 4 <= 60) 65 { 66 x[i][j] += 4; 67 for (int d = 0; d < 4; ++d) 68 { 69 _i = i + dx[d], _j = j + dy[d]; 70 if (check(_i, _j)) --x[_i][_j], ++y[_i][_j]; 71 } 72 } 73 } 74 } 75 } 76 vector<pair<int, int> > ans; 77 for (int i = 1; i <= n; ++i) 78 { 79 for (int j = 1; j <= m; ++j) 80 { 81 if (a[i][j] == -1) continue; 82 assert(x[i][j] >= 0); 83 while (x[i][j] > 0) --x[i][j], ans.push_back(make_pair(i, j)); 84 } 85 } 86 printf("%d\n", ans.size()); 87 for (int i = 0; i < (int)ans.size(); ++i) 88 printf("%d %d\n", ans[i].first, ans[i].second); 89 } 90 91 return 0; 92 }