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 
 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         vectorint, 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 }

 

转载于:https://www.cnblogs.com/vb4896/p/8820356.html

你可能感兴趣的:(Crosses Puzzles zoj 4018 (zju校赛))