1. 程式人生 > 實用技巧 >【幾何+模擬】二次元變換 計蒜客 - T3213

【幾何+模擬】二次元變換 計蒜客 - T3213

題目

aslky有一個n×n的矩形,每個位置上都有一個數,有q次操作,每次他會讓你上下翻轉(UD),左右反轉(LR),順時針旋轉90∘(SZ),逆時針旋轉90∘(NZ),請你輸出最後的矩形。

輸入格式

第一行,兩個數n,q。

接下來n行,每行n個數,代表矩形。

接下來q行,每行一個字串,代表操作。

輸出格式

n行,每行n個數代表矩形。

資料規模與約定

對於10%的資料,1≤n≤10;

對於100%的資料,1≤n≤1000,1≤q≤1e6

Sample Input

3 3
1 2 3
1 2 3
1 2 3
NZ
SZ
SZ

Sample Output

1 1 1
2 2 2
3 3 3

題解

一次操作的複雜度為O(N^2),直接模擬複雜度為O(q*N^2),n<=1000,q<=1e6,計算次數為1e12,直接模擬必定超時,需要優化。如果該題無翻轉變換隻有旋轉變化就簡單了,SZ和NZ可以相互抵消,最後再對結果模4,就能大程度的優化操作次數。但加上翻轉變化,那就要考慮兩種翻轉變化的優化,以及翻轉變化和旋轉變化的相互影響,還有影象在這幾種變化中擁有多少種狀態。

通過畫圖模擬,我們可以得出,影象在這2種變化中共有8種狀態,首先是,原影象只通過旋轉可以有4種狀態,加上翻轉變化後,我們發現無論是上下翻轉還是左右翻轉,其實都是把影象變成映象面,影象在這種映象面同樣有4種形態,合起來共8種。

對翻轉變化的優化,我們容易發現,同時經過兩次上下翻轉或者左右翻轉後影象復原,所以,單向翻轉變化可以模2.再對翻轉變化進行研究,發現影象經過一次上下翻轉一次左右翻轉,會脫離映象圖變為原圖。歸納:一個影象經過一次翻轉變化後變為映象圖,這個映象圖再經過一次翻轉變換後復原為原圖。

接下來是研究兩種變化之間的關係。容易發現上下翻轉的影象通過旋轉可變為左右翻轉的影象,通過這個性質我們可以得出一個等式:LR=UD+2*SZ. 通過這個等式我們可以把所有的左右翻轉變化變為上下翻轉變化加上旋轉變化。

剩下的就是考慮在翻轉變化後旋轉變化有何影響,容易知道,在映象圖狀態時,對影象的順時針旋轉將相當於原影象的逆時針旋轉,逆時針亦然。

暴力模板

#include<iostream>
#include<algorithm>
using namespace std; int s[][];
int f[][];
int n;
void UD() {
for (int i = ; i < n / ; i++) {
for (int j = ; j < n; j++) {
swap(s[i][j], s[n - i - ][j]);
}
}
}
void LR() {
for (int i = ; i < n / ; i++) {
for (int j = ; j < n; j++) {
swap(s[j][i], s[j][n - i - ]);
}
} }
void SZ() { for (int i = ; i < n; i++) {
for (int j = ; j < n; j++) {
f[j][n - i - ] = s[i][j];
}
}
for (int i = ; i < n; i++) {
for (int j = ; j < n; j++) {
s[i][j] = f[i][j];
}
}
} void NZ() {
for (int i = ; i < n; i++) {
for (int j = ; j < n; j++) {
f[n - - j][i] = s[i][j];
}
}
for (int i = ; i < n; i++) {
for (int j = ; j < n; j++) {
s[i][j] = f[i][j];
}
} } void pr() {
for (int i = ; i < n; i++) {
for (int j = ; j < n; j++)
cout << s[i][j] << ' ';
cout << '\n';
}
} int main() {
ios::sync_with_stdio();
int q;
char com[];
cin >> n >> q;
for (int i = ; i < n; i++)
for (int j = ; j < n; j++)
cin >> s[i][j]; while (q--) {
cin >> com;
switch (com[]) {
case 'U':
UD();
break;
case 'L':
LR();
break;
case 'S':
SZ();
break;
case 'N':
NZ();
break;
}
/*pr();
cout << '\n';*/
} pr();
}

資料生成

#include<iostream>
#include<time.h>
#include<algorithm>
#include<map>
using namespace std;
int r(int l, int r) {
return rand() % (r+ - l) + l;
}
char s[][] = { "UD","LR","SZ","NZ" }; int main() {
ios::sync_with_stdio(); srand(time());
int n=, q=r(,);
int k = ;
cout << n << ' ' << q << '\n';
cout << "1 2\n";
cout << "4 3\n";
while (q--) {
cout << s[r(, )] << '\n';
}
return ;
}

AC程式碼

#include<iostream>
#include<algorithm>
using namespace std; int s[][];
int f[][];
int n;
void UD() {
for (int i = ; i < n/; i++) {
for (int j = ; j < n; j++) {
swap(s[i][j], s[n - i-][j]);
}
}
} void SZ() { for (int i = ; i < n; i++) {
for (int j = ; j < n; j++) {
f[j][n - i-] = s[i][j];
}
}
for (int i = ; i < n; i++) {
for (int j = ; j < n; j++) {
s[i][j] = f[i][j];
}
}
} void pr() {
for (int i = ; i < n; i++) {
for (int j = ; j < n; j++)
cout << s[i][j] << ' ';
cout << '\n';
}
} int main() {
ios::sync_with_stdio();
int q;
char com[];
cin >> n >> q;
for (int i = ; i < n; i++)
for (int j = ; j < n; j++)
cin >> s[i][j]; int flag = ;
int sz = ;
while (q--) {
cin >> com;
switch (com[]){
case 'U':
if (flag == ) {
sz++;
flag *= -;
}
else {
sz--;
flag *= -;
}
break;
case 'L':
if (flag == ) {
sz--;
flag *= -;
}
else {
sz++;
flag *= -;
}
break; case 'S':
if (flag == ) {
sz++;
}
else {
sz--;
}
break;
case 'N':
if (flag == ) {
sz--;
}
else {
sz++;
}
break;
}
if(sz>=)
sz %= ;
else {
sz %= ;
sz += ;
}
}
if (flag==) {
while (sz--)SZ();
}
else {
sz--;
sz += ;
sz %= ;
while (sz--)SZ();
UD(); } pr(); }