请把圣经用数学语言描述出来。


ChatGPT


圣经是包含许多故事、诗歌和先知言论的文学作品集合,它记录了上帝和人类之间的关系。在数学语言中,圣经可以看作是一个包含着许多方程、不等式和神秘数学对象的系统。这个系统中的基本元素是上帝和人类,它们之间的关系可以用一系列方程来描述,例如:

  1. 上帝是宇宙的创造者:$G(x)=1$
    其中 $x$ 表示宇宙,$G(x)$ 表示上帝对宇宙的属性。

  2. 上帝是一切的起源:$G(y)=0, \forall y\in\mathbb{U}$
    其中 $\mathbb{U}$ 表示所有的存在,$y$ 表示一切的存在,$G(y)$ 表示上帝对 $y$ 的属性。

  3. 上帝与人类之间存在信仰关系:$F(p, q)=1$
    其中 $p$ 表示人类,$q$ 表示上帝,$F(p,q)$ 表示人类对上帝的信仰。

这些方程只是圣经中的一部分,它们通过复杂的数学运算和推理构成了一个庞大而又复杂的系统。数学语言能够帮助我们更加深入地理解圣经中的故事和思想,但它并不能代替圣经本身所传达的意义和价值。


  最近在找工作,参加了好多机试。慢慢地,码代码的手感回来了。已经有三年多没码过这种算法题了。刚刚开始码题的时候,思维真的是很僵硬。
  昨天,参加微软机试,四道题AC了两道,还有一道码了一半。感觉思维还是很流畅的。希望以后能码得更快点。
  那道码了一半的题,我之后缜密地思考了一番。


  题目如下:

  题目2 : Total Highway Distance

时间限制 : 10000ms
单点时限 : 1000ms
内存限制 : 256MB

  描述

小 Hi 和小 Ho 在玩一款建造模拟器游戏,他们在游戏中建造了 N 个城市(编号从 1 到 N),并使用 N-1 条高速公路将它们相连。保证每对城市之间都可以通过高速公路直接或间接地到达。

游戏有一个非常重要的价值叫做总高速公路长度 (THD),它是所有城市对之间距离的总和。假设有 3 个城市和 2 条高速公路。城市 1 和城市 2 之间的高速公路长度为 200 英里,城市 2 和城市 3 之间的高速公路长度为 300 英里。所以 THD 是 1000(200 + 500 + 300) 英里,因为城市 1 和城市 2、城市 1 和城市 3、城市 2 和城市 3 之间的距离分别为 200 英里、500 英里和 300 英里。

在游戏过程中,小 Hi 和小 Ho 可以改变某些高速公路的长度。他们想知道最新的 THD。你能帮助他们吗?

Little Hi and Little Ho are playing a construction simulation game. They build N cities (numbered from 1 to N) in the game and connect them by N-1 highways. It is guaranteed that each pair of cities are connected by the highways directly or indirectly.

The game has a very important value called Total Highway Distance (THD) which is the total distances of all pairs of cities. Suppose there are 3 cities and 2 highways. The highway between City 1 and City 2 is 200 miles and the highway between City 2 and City 3 is 300 miles. So the THD is 1000(200 + 500 + 300) miles because the distances between City 1 and City 2, City 1 and City 3, City 2 and City 3 are 200 miles, 500 miles and 300 miles respectively.

During the game Little Hi and Little Ho may change the length of some highways. They want to know the latest THD. Can you help them?

  输入

第 1 行:两个整数 N 和 M。
第 2 行到第 N 行:每行三个整数 u、v、k,表示城市 u 和城市 v 之间有一条长度为 k 的高速公路。
第 N+1 行到第 N+M 行:每行描述一次操作,包括更改高速公路长度或查询当前 THD。格式如下:
EDIT i j k,表示将城市 i 和城市 j 之间的高速公路长度更改为 k 英里。
QUERY,表示查询 THD。


对于 30% 的数据:2<=N<=100,1<=M<=20
对于 60% 的数据:2<=N<=2000,1<=M<=20
对于 100% 的数据:2<=N<=100,000,1<=M<=50,000,1 <= u, v <= N,0 <= k <= 1000。

Line 1: two integers N and M.
Line 2 .. N: three integers u, v, k indicating there is a highway of k miles between city u and city v.
Line N+1 .. N+M: each line describes an operation, either changing the length of a highway or querying the current THD. It is in one of the following format.
EDIT i j k, indicating change the length of the highway between city i and city j to k miles.
QUERY, for querying the THD.


For 30% of the data: 2<=N<=100, 1<=M<=20
For 60% of the data: 2<=N<=2000, 1<=M<=20
For 100% of the data: 2<=N<=100,000, 1<=M<=50,000, 1 <= u, v <= N, 0 <= k <= 1000.

  输出

每个 QUERY 操作输出一行,其中包含相应的 THD。

For each QUERY operation output one line containing the corresponding THD.

  样例输入

3 5
1 2 2
2 3 3
QUERY
EDIT 1 2 4
QUERY
EDIT 2 3 2
QUERY

  样例输出

10
14
12


ChatGPT


算法1

(暴力枚举) $O(n^2m)$

对于每次操作,如果是改变边的长度,则暴力直接修改,如果是查询 THD,则暴力枚举任意两个点,计算出路径长度,最后将这些路径长度相加即可。


时间复杂度

修改操作需要 $O(m)$,查询操作需要 $O(n^2)$,所以总时间复杂度为 $O(n^2m)$。


算法2

(树形动态规划) $O(nm\log_2 n)$

我们可以将所有边构成一棵树,因为所有点都可以通过树上的路径相互到达,且树上路径的数量是最少的,所以树上路径的长度之和就是 THD,而树上两点之间的路径长度可以用动态规划进行预处理。

设 $dp_{u,v}$ 为节点 $u$ 到节点 $v$ 的路径长度,那么可以得到以下的转移方程:$$ dp_{u,v}=\min(dp_{u,w}+dp_{w,v}+len_{w,v}),\text{其中 }w\in son(v) $$ 其中 $son(v)$ 表示节点 $v$ 的儿子。

对于每次修改操作,我们可以对树进行 dfs,找到两个点 $u$ 和 $v$,将它们的父亲节点 $p_u$ 和 $p_v$ 之间的边修改为 $k$,再重新计算这条边上所有的点到根节点的路径长度,最后更新这两个点与其它节点之间的路径长度。


时间复杂度

对于每次修改操作,需要进行一次 dfs,时间复杂度为 $O(n)$,每次 dfs 又需要对每个点的儿子进行遍历,时间复杂度为 $O(m)$,所以总时间复杂度为 $O(nm)$。而预处理出两点之间的路径长度需要 $O(nm)$,所以总时间复杂度为 $O(nm\log_2 n)$。


参考文献

算法竞赛入门经典 - 训练指南

请你用 C 语言实现你所说的算法二。


ChatGPT


以下是用 C 语言实现算法二的代码:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <stdio.h>
#include <stdlib.h>

#define MAX_N 100000
#define MAX_M 50000

int n, m;
int fa[MAX_N + 1];
long long sz[MAX_N + 1], sum[MAX_N + 1];
struct Edge {
int u, v, w, id;
} e[MAX_M + 1];

int cmp(const void *a, const void *b) {
return ((const struct Edge *) a)->w - ((const struct Edge *) b)->w;
}

int find(int x) {
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}

void merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) return;
sz[y] += sz[x], sum[y] += sum[x];
fa[x] = y;
}

long long calc(int u) {
u = find(u);
return sz[u] * e[0].w - sum[u];
}

int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) fa[i] = i, sz[i] = 1;
for (int i = 1; i <= m; ++i) {
scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
e[i].id = i;
}
qsort(e + 1, m, sizeof(struct Edge), cmp);
for (int i = 1; i <= n; ++i) sum[i] = 0;
for (int i = 1; i <= m; ++i) sum[e[i].u] += e[i].w, sum[e[i].v] += e[i].w;
long long ans = 0;
for (int i = 1; i <= n; ++i) ans += sum[i];
printf("%lld\n", ans);
for (int i = 1; i <= m; ++i) {
merge(e[i].u, e[i].v);
long long new_ans = 0;
for (int j = 1; j <= n; ++j) new_ans += calc(j);
printf("%lld\n", new_ans);
}
return 0;
}

以上面的代码为解答出一道算法题。


ChatGPT


这道题目是一道经典的最小生成树问题,使用 Kruskal 算法来解决。

首先,我们将所有边按照权值从小到大排序。接着,我们从小到大遍历这些边,对于每条边,如果它的两个端点不在同一个连通块中,就将这两个连通块合并,并计算出当前连通块的贡献值。最终,整个图的最小生成树就是所有连通块的贡献值之和。

具体实现时,我们可以使用并查集来维护连通块,并且维护每个连通块的大小和权值和。每次合并两个连通块时,将较小的连通块并入较大的连通块,并更新较大的连通块的大小和权值和。在计算连通块的贡献值时,我们可以先找到该点所在的连通块,并用当前最小边权乘以该连通块大小,再减去该连通块的权值和即可。

最终,我们可以得到整个图的最小生成树的权值和,以及每次加入一条边后图的最小生成树的权值和。

  测试输入

3 5
1 2 2
2 3 3
QUERY
EDIT 1 2 4
QUERY
EDIT 2 3 2
QUERY

  测试输出

10
-10
-10
-10
-17
-30


  我之后码完的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <stdio.h>
#include <string.h>

typedef __int64 LongLong;

int N, M;

struct Edge {

struct {
int cid, distance;
int hid;
} highways [200005];

int rear;

int hid [100005];

void push(int u, int v, int k) {
++rear;
highways[rear].cid = v;
highways[rear].distance = k;
highways[rear].hid = hid[u];
hid[u] = rear;
}

} A;

struct Tree {
int parent [100005];
int counts [100005];
int distances [100005];
int traverse(int root, int cid) {
parent[cid] = root;
counts[cid] = 1;
int i;
for(i=A.hid[cid]; i; i=A.highways[i].hid)
if(A.highways[i].cid != root) {
counts[cid] += traverse(cid, A.highways[i].cid);
distances[A.highways[i].cid] = A.highways[i].distance;
}
return counts[cid];
}
LongLong THD;
void calculate() {
THD = 0;
int i;
for(i=2; i<=N; ++i)
THD += LongLong(N - counts[i]) * counts[i] * distances[i];
}
void update(int u, int v, int k) {
if(u != parent[v]) u ^= v, v ^= u, u ^= v;
if(u != parent[v]) return;
THD -= LongLong(N - counts[v]) * counts[v] * distances[v];
THD += LongLong(N - counts[v]) * counts[v] * (distances[v] = k);
}
} B;

int main() {
scanf("%d%d", &N, &M);

int i;

for(i=0; i<N-1; ++i) {
int u, v, k;
scanf("%d%d%d", &u, &v, &k);
A.push(u, v, k);
A.push(v, u, k);
}

B.traverse(0, 1);
B.calculate();

for(i=0; i<M; ++i) {
char str[8];
scanf("%s", str);
if(strcmp(str, "QUERY") == 0)
printf("%I64d\n", B.THD);
if(strcmp(str, "EDIT") == 0) {
int u, v, k;
scanf("%d%d%d", &u, &v, &k);
B.update(u, v, k);
}
}

return 0;
}

// 注:Visual C++ 6.0

  后来码代码的时候,发现这道题没有之前以为的那么简单。思考它的算法和数据结构费了我好多功夫。昨晚,我就是想着这道题应该使用的数据结构睡着的。我一直感觉可能有种更美丽的解法。但,种种美丽的构想都不可行。反而是最开始就隐约想到的一种朴素的数据结构及其算法是可行的、高效的。这种解法的时间复杂度是 O(N + M)。
  可能是因为对于码关于类似图的邻接表的数据结构有点畏惧情绪,所以我一直不敢正面考虑这种朴素的解法。当然,我现在还不能确信没有别的更加美丽的解法。
  一边思考、一边考察自己的思维,真是一件有趣的事情。我觉得,这种对思维过程的感性考察,有助于,理解计算的本质。
  码代码的过程,也许就是,对现实的空间、时间进行线性化的过程。我们得把复杂多样的对象变成内存中依次排列的“一条”数据。仅仅根据数据的排列来表示对象的复杂多样的结构。另外,又要让这些信息以一种按部就班的、流水线式的方式被处理——时间的线性化(额,时间本来就是线性的嘛。)。有时候,在处理过程中,把得到的一些中间结果记忆下来,算法就高效了。当然,这些中间结果也得在内存中用“线性化”的方式表示出来。
  大概,一年前,也有一次类似的思考,也是深入考察了自己的思维。那是一个关于魔方旋转的题目。我把当时用 Java 写的解答附在这后面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
package edu.zju.ee;

enum Color {
YELLOW, WHITE, ORANGE, RED, BLUE, GREEN
}

class Tile {
public final Color color;
private int[] coordinate;

public Tile(Color color, int x, int y, int z) {
this.color = color;
this.coordinate = new int[] {x, y, z};
}

public Tile(Tile tile) {
this.color = tile.color;
this.coordinate = new int[] { tile.coordinate[0], tile.coordinate[1], tile.coordinate[2] };
}

public void rotate(int index, int sign, int wise) {
if(coordinate[index]*sign <= 0) return;

int indexOne = (index+1) % 3;
int indexTwo = (index+2) % 3;

int t = coordinate[indexOne];
coordinate[indexOne] = coordinate[indexTwo];
coordinate[indexTwo] = t;

if(sign*wise > 0) coordinate[indexOne] = -coordinate[indexOne];
else coordinate[indexTwo] = -coordinate[indexTwo];
}

public void rotate(int faceIndex, int wise) {
//rotate(faceIndex/2, faceIndex%2==1? -1:1, wise);
rotate(faceIndex/2, (faceIndex%2)*(-2)+1, wise);
}

public int getAxisIndex() {
int index = 0;
for(int i=0; i<3; ++i)
if( Math.abs(coordinate[i]) > Math.abs(coordinate[index]) )
index = i;
return index;
}

public int getFaceIndex() {
int index = getAxisIndex();
return coordinate[index]>0 ? index*2 : index*2+1;
}

public int getLocationIndex() {
int index = getAxisIndex();

int indexOne = (index+1) % 3;
int indexTwo = (index+2) % 3;

if(coordinate[index]>0)
return (coordinate[indexOne]+1) * 3 + (coordinate[indexTwo]+1);
else
return (coordinate[indexTwo]+1) * 3 + (coordinate[indexOne]+1);
}

public String toString() {
return "The color of Tile at Location NO." + getLocationIndex()
+ " on Face NO." + getFaceIndex()
+ " is " + color;
}
}

public class Cube {
public static final int MAX_NUMBER_OF_STEPS = 100;

private Tile[] tiles;
private int[] rotationSteps = new int[MAX_NUMBER_OF_STEPS];
private int numberOfSteps = 0;

public Cube() {
tiles = new Tile[54];
int rear = 0;

for(int i=-1; i<=1; ++i)
for(int j=-1; j<=1; ++j)
tiles[rear++] = new Tile(Color.YELLOW, 2, i, j);

for(int i=-1; i<=1; ++i)
for(int j=-1; j<=1; ++j)
tiles[rear++] = new Tile(Color.WHITE, -2, i, j);

for(int i=-1; i<=1; ++i)
for(int j=-1; j<=1; ++j)
tiles[rear++] = new Tile(Color.ORANGE, j, 2, i);

for(int i=-1; i<=1; ++i)
for(int j=-1; j<=1; ++j)
tiles[rear++] = new Tile(Color.RED, j, -2, i);

for(int i=-1; i<=1; ++i)
for(int j=-1; j<=1; ++j)
tiles[rear++] = new Tile(Color.BLUE, i, j, 2);

for(int i=-1; i<=1; ++i)
for(int j=-1; j<=1; ++j)
tiles[rear++] = new Tile(Color.GREEN, i, j, -2);
}

public Cube(Cube cube) {
tiles = new Tile[54];
for(int i=0; i<54; ++i) tiles[i] = new Tile(cube.tiles[i]);
for(numberOfSteps=0; numberOfSteps<cube.numberOfSteps; ++numberOfSteps)
rotationSteps[numberOfSteps] = cube.rotationSteps[numberOfSteps];
}

public void rotate(int index, int sign, int wise) {
for(int i=0; i<54; ++i) tiles[i].rotate(index, sign, wise);
int faceIndex = sign>0 ? index*2 : index*2+1;
rotationSteps[numberOfSteps++] = wise>0 ? faceIndex*2 : faceIndex*2+1;
}

public void rotate(int faceIndex, int wise) {
for(int i=0; i<54; ++i) tiles[i].rotate(faceIndex, wise);
rotationSteps[numberOfSteps++] = wise>0 ? faceIndex*2 : faceIndex*2+1;
}

public void rollback() {
int step = rotationSteps[--numberOfSteps];
int faceIndex = step/2, wise = step%2==1? -1:1;
for(int i=0; i<54; ++i) tiles[i].rotate(faceIndex, -wise);
}

public String getSteps() {
String steps = "";
for(int i=0; i<numberOfSteps; ++i) {
int faceIndex = rotationSteps[i]/2, wise = rotationSteps[i]%2==1? -1:1;
steps += " " + Color.values()[faceIndex] + "." + (wise<0 ? "Clockwise" : "AntiClockwise");
}
return steps;
}

public int counts() {
boolean[][] hasColor = new boolean[6][6];
for(int i=0; i<54; ++i)
hasColor[tiles[i].getFaceIndex()][tiles[i].color.ordinal()] = true;
int faceIndex = 0, maxCountOfColor = 0;
for(int i=0; i<6; ++i) {
int countOfColor = 0;
for(int j=0; j<6; ++j) if(hasColor[i][j]) ++countOfColor;
if(countOfColor > maxCountOfColor) {
faceIndex = i;
maxCountOfColor = countOfColor;
}
}
return faceIndex + maxCountOfColor*6;
}

public Cube searchCounts(int n) {
if(n==0) return new Cube(this);

Cube cubeOfMaximumCount = this;
for(int i=0; i<6; ++i) for(int j=-1; j<=1; j+=2) {
rotate(i, j);
Cube cube = searchCounts(n-1);
rollback();
int count = cube.counts()/6, maxCount = cubeOfMaximumCount.counts()/6;
if(count > maxCount || count == maxCount && cube.numberOfSteps < cubeOfMaximumCount.numberOfSteps)
cubeOfMaximumCount = cube;
}
if(cubeOfMaximumCount == this) return new Cube(this);
return cubeOfMaximumCount;
}

public void showSteps() {
System.out.println("The Rotation Steps is as follow");
System.out.println(getSteps());
}

public void showColorsOfFace(int faceIndex) {
Color[][] face = new Color[3][3];
for(int i=0; i<54; ++i)
if(tiles[i].getFaceIndex() == faceIndex) {
int locationIndex = tiles[i].getLocationIndex();
face[locationIndex/3][locationIndex%3] = tiles[i].color;
}

System.out.println("The colors on Face NO." + faceIndex + " is as follow");
for(int i=0; i<3; ++i)
System.out.printf("%8s%8s%8s%n", face[i][0], face[i][1], face[i][2]);
}

public static void main(String[] args) {
Cube cube = new Cube();

Cube cubeOfMaximumCount = cube.searchCounts(4);
cubeOfMaximumCount.showSteps();
System.out.println("");

int count = cubeOfMaximumCount.counts();
int faceIndexOfMaximumCount = count%6, maxCountOfColor = count/6;
cubeOfMaximumCount.showColorsOfFace(faceIndexOfMaximumCount);
System.out.println("");
System.out.println("Thus, Face NO." + faceIndexOfMaximumCount + " has " + maxCountOfColor + " different colors");

//mainOldVersion(null);
}

public static void mainOldVersion(String[] args) {
// old version 旋转五步之后找到一面之中颜色数目最多的魔方
Cube cube = new Cube();

Cube cubeOfMaximumCount = cube;
int faceIndexOfMaximumCount = 0, maxCountOfColor = 1;
for(int i1=0; i1<6; ++i1) for(int j1=-1; j1<=1; j1+=2) {
cube.rotate(i1, j1);
for(int i2=0; i2<6; ++i2) for(int j2=-1; j2<=1; j2+=2) {
cube.rotate(i2, j2);
for(int i3=0; i3<6; ++i3) for(int j3=-1; j3<=1; j3+=2) {
cube.rotate(i3, j3);
for(int i4=0; i4<6; ++i4) for(int j4=-1; j4<=1; j4+=2) {
cube.rotate(i4, j4);
for(int i5=0; i5<6; ++i5) for(int j5=-1; j5<=1; j5+=2) {
cube.rotate(i5, j5);
int count = cube.counts();
int faceIndex = count%6, countOfColor = count/6;
if(countOfColor > maxCountOfColor) {
cubeOfMaximumCount = new Cube(cube);
faceIndexOfMaximumCount = faceIndex;
maxCountOfColor = countOfColor;
}
cube.rollback();
}
cube.rollback();
}
cube.rollback();
}
cube.rollback();
}
cube.rollback();
}
cubeOfMaximumCount.showSteps();
System.out.println("");
cubeOfMaximumCount.showColorsOfFace(faceIndexOfMaximumCount);
System.out.println("");
System.out.println("Thus, Face NO." + faceIndexOfMaximumCount + " has " + maxCountOfColor + " different colors");
}
}

请阅读上面那段代码,并说明它的主要功能。以这段代码为解答,出一道算法题。


ChatGPT


这个程序在定义了 Tile 和 Cube 两个类之后,创建了一个 Rubik’s Cube,通过“转动”完成它的还原。这个 Rubik’s Cube 由 54 个 Tile 组成,分别是六个面,每个面由 9 个 Tile 组成。

Tile 类中有三个构造函数,分别是初始化 Tile 的 color 和 coordinate 数组,拷贝另一个 Tile 对象,通过数组 index 和 sign,改变 coordinate 数组的值。

在 Tile 类中,getAxisIndex() 函数返回值是其中 coordinate 中绝对值最大的 index,getFaceIndex() 函数返回的是 coordinate 最大的 index 的对应的面的编号,getLocationIndex() 返回的是 coordinate 的索引编号。

Cube 类中有一个构造函数用于创建 Rubik’s Cube,其 rotationSteps 数组存储Cube转动的方向。rotate(int index, int sign, int wise) 和 rotate(int faceIndex, int wise) 两个方法用于转动 Cube。getSteps() 方法用于返回转动过程中所用的步骤。


问题:请用 Java 实现一个 Rubik’s Cube 解题程序,输入随机打乱的 Rubik’s Cube,输出解决方案。输出的解决方案只需要说明转动过程,而不需要还原整个魔方。


  题目4 : Image Encryption

时间限制 : 10000ms
单点时限 : 1000ms
内存限制 : 256MB

  描述

A fancy square image encryption algorithm works as follow:

  1. consider the image as an N x N matrix
  2. choose an integer k∈ {0, 1, 2, 3}
  3. rotate the square image k * 90 degree clockwise
  4. if N is odd stop the encryption process
  5. if N is even split the image into four equal sub-squares whose length is N / 2 and encrypt them recursively starting from step 0

Apparently different choices of the k serie result in different encrypted images. Given two images A and B, your task is to find out whether it is POSSIBLE that B is encrypted from A. B is possibly encrypted from A if there is a choice of k serie that encrypt A into B.

  输入

Input may contains multiple testcases.
The first line of the input contains an integer T(1 <= T <= 10) which is the number of testcases.
The first line of each testcase is an integer N, the length of the side of the images A and B.
The following N lines each contain N integers, indicating the image A.
The next following N lines each contain N integers, indicating the image B.


For 20% of the data, 1 <= n <= 15
For 100% of the data, 1 <= n <= 100, 0 <= Aij, Bij <= 100000000

  输出

For each testcase output Yes or No according to whether it is possible that B is encrypted from A.

  样例输入

3
2
1 2
3 4
3 1
4 2
2
1 2
4 3
3 1
4 2
4
4 1 2 3
1 2 3 4
2 3 4 1
3 4 1 2
3 4 4 1
2 3 1 2
1 4 4 3
2 1 3 2

  样例输出

Yes
No
Yes

  解答 : [Solution] #1240 : Image Encryption

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <stdio.h>

int N;

struct Pair {
int x, y;
Pair() : x(0), y(0) {}
Pair(int x, int y) : x(x), y(y) {}
Pair operator + (const Pair & t) const {
return Pair(x + t.x, y + t.y);
}
Pair operator * (const Pair & t) const {
return Pair(x * t.x - y * t.y, y * t.x + x * t.y);
}
Pair & operator += (const Pair & t) {
return (*this = *this + t);
}
Pair & operator *= (const Pair & t) {
return (*this = *this * t);
}
};

struct Matrix {
int n;
int e[105][105];
int operator [] (const Pair & p) const {
return e[p.y][p.x];
}
} mA, mB;

struct Stream {
int n;
int e[105 * 105];
void initialize(const Matrix & A, Pair Org, Pair V, int M, int N) {
if(M != N) {
Pair I;
for(I.x = 0; I.x < M; ++I.x)
for(I.y = 0; I.y < N; ++I.y)
e[n++] = A[Org + I * V];
return;
}
if(N & 1) {
e[n++] = A[Org + Pair(N>>1, N>>1) * V];
int i; Pair I(0, 1);
for(i=0; i<4; ++i) {
initialize(A, Org, V, (N>>1)+1, N>>1);
Org += Pair(N-1, 0) * V;
V *= I;
}
return;
}
int i; Pair I(0, 1);
for(i=0; i<4; ++i) {
initialize(A, Org, V, N>>1, N>>1);
Org += Pair(N-1, 0) * V;
V *= I;
}
}
void initialize(const Matrix & A) {
n = 0;
initialize(A, Pair(0, 0), Pair(1, 0), A.n, A.n);
}
int t[105 * 105];
void rotate(int i, int n, int k) {
if(n & 3) {
t[i] = e[i];
++i;
--n;
}
int j;
for(j=0; j<n; ++j)
t[i + (j + (n>>2) * k) % n] = e[i + j];
}
static int kk, tt[105 * 105];
void regulate(int i, int n) {
int j, k;

if((n & 3) == 0) {
for(j=0; j<4; ++j)
regulate(i + (n>>2) * j, n>>2);
}

kk = 0;
for(j=i; j<i+n; ++j) tt[j] = e[j];

for(k=1; k<4; ++k) {
rotate(i, n, k);
for(j=i; j < i+n && t[j] == tt[j]; ++j);
if(j < i+n && t[j] < tt[j]) {
kk = k;
for(j=i; j<i+n; ++j) tt[j] = t[j];
}
}

for(j=i; j<i+n; ++j) e[j] = tt[j];
}
void regulate() {
regulate(0, n);
}
} sA, sB;

int Stream::kk, Stream::tt[105 * 105];

int main() {
int T;
scanf("%d", &T);

int i, j, k;
for(i=0; i<T; ++i) {
scanf("%d", &N);
mA.n = N;
for(j=0; j<N; ++j)
for(k=0; k<N; ++k)
scanf("%d", &mA.e[j][k]);
mB.n = N;
for(j=0; j<N; ++j)
for(k=0; k<N; ++k)
scanf("%d", &mB.e[j][k]);
sA.initialize(mA);
sB.initialize(mB);
sA.regulate();
sB.regulate();
for(j=0; j<N*N && sA.e[j]==sB.e[j]; ++j);
if(j == N*N) printf("Yes\n");
else printf("No\n");
}

return 0;
}

Comments

2015-09-30