P2200 题解
思路
本题涉及到的操作为”横向交换“与”纵向交换“。
实际上,”横向交换“即改变某张卡牌的列号,而”纵向交换“即改变某张卡牌的行号。
容易想到,既然连续进行同种交换不产生额外花费,我们应当试着先统一改变列号,再统一改变行号(或者反过来)。
可是,从第一个样例中我们就可以发现,这样的操作中可能会有冲突。例如,两张卡牌原先所处的行相同而目标列相同,这会使得按列归位时两张卡牌无法同时被移动到目标列。当两张卡牌原先所处的列相同而目标行相同时,也会产生类似的冲突。
通过手动模拟,可以发现,在出现冲突时,我们可以先正常地横向与纵向移动其它卡牌使其归位,再用一次额外的横向或纵向(注意,两种操作中有一种即可,另一种操作可以在之前提前进行)使冲突的卡牌归位。
还需要注意的是,我们需要尽可能减少冲突从而使花费尽可能小。这意味着当同种类型的卡牌有两张出现时,除非两张都会与某张卡牌产生冲突,否则我们就不将其视为冲突;另外,如果只有一个方向的冲突(如只有横向或纵向交换导致冲突),我们完全可以先进行另一个方向的交换,此时也不视为冲突。
最终的代码模拟上述过程即可。
代码
#include <bits/stdc++.h>
using namespace std;
#define CARDID 100003
#define N 303
int cxpos[CARDID],cypos[CARDID],cx2pos[CARDID],cy2pos[CARDID]; // 注意:x 纵向,y 横向。
int oxpos[CARDID],oypos[CARDID],ox2pos[CARDID],oy2pos[CARDID],targets[N][N]; // 卡牌的原先位置
int exist[CARDID] = {}, rexist[CARDID] = {}; // 有几张卡牌
bool counter[CARDID] = {};
bool failure = false;
bool cpx = false, cpy = false; // 是否有冲突
int xmove = 0, ymove = 0; // 是否需要进行该方向的交换
inline int mini(int x, int y) {
return x<y?x:y;
}
inline bool judge(int x1, int y1, int x2, int y2) {
int &type1 = targets[x1][y1], &type2 = targets[x2][y2];
bool f1, f2;
switch (rexist[type1]) {
case 1:
switch (rexist[type2]) {
case 1:
return (x1 == x2) ? (cypos[type1] == cypos[type2]) : (cxpos[type1] == cxpos[type2]);
break;
case 2:
return (x1 == x2) ? (cypos[type1] == cypos[type2] && cypos[type1] == cy2pos[type2]
&& oxpos[type1] == oxpos[type2] && oxpos[type1] == ox2pos[type2])
: (cxpos[type1] == cxpos[type2] && cxpos[type1] == cx2pos[type2]
&& oypos[type1] == oypos[type2] && oypos[type1] == oy2pos[type2]);
break;
}
break;
case 2:
switch (rexist[type2]) {
case 1:
return (x1 == x2) ? (cypos[type1] == cypos[type2] && cy2pos[type1] == cypos[type2]
&& oxpos[type1] == oxpos[type2] && ox2pos[type1] == oxpos[type2])
: (cxpos[type1] == cxpos[type2] && cx2pos[type1] == cxpos[type2]
&& oypos[type1] == oypos[type2] && oy2pos[type1] == oypos[type2]);
break;
case 2:
f1 = (x1 == x2) ? (cypos[type1] == cypos[type2] && cy2pos[type1] == cypos[type2]
&& oxpos[type1] == oxpos[type2] && ox2pos[type1] == oxpos[type2])
: (cxpos[type1] == cxpos[type2] && cx2pos[type1] == cxpos[type2]
&& oypos[type1] == oypos[type2] && oy2pos[type1] == oypos[type2]);
f2 = (x1 == x2) ? (cypos[type1] == cypos[type2] && cypos[type1] == cy2pos[type2]
&& oxpos[type1] == oxpos[type2] && oxpos[type1] == ox2pos[type2])
: (cxpos[type1] == cxpos[type2] && cxpos[type1] == cx2pos[type2]
&& oypos[type1] == oypos[type2] && oypos[type1] == oy2pos[type2]);
return f1 && f2;
break;
}
break;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,a,b,tmp;
cin>>n>>a>>b;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin>>tmp;
targets[i][j] = tmp;
if (!exist[tmp]) {
oxpos[tmp] = i;
oypos[tmp] = j;
} else {
ox2pos[tmp] = i;
oy2pos[tmp] = j;
}
exist[tmp]++;
rexist[tmp]++;
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin>>tmp;
if (!exist[tmp]) {
failure = true;
} else {
exist[tmp]--;
if (oxpos[tmp] != i && ox2pos[tmp] != i) {
xmove = 1;
}
if (oypos[tmp] != j && oy2pos[tmp] != j) {
ymove = 1;
}
if (counter[tmp]) {
cx2pos[tmp] = i;
cy2pos[tmp] = j;
} else {
cxpos[tmp] = i;
cypos[tmp] = j;
}
counter[tmp] = true;
}
}
}
if (failure) {
cout<<"Fail"<<endl;
return 0;
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < i; k++) {
if (judge(i,j,k,j)) {
cpx = true;
}
}
for (int k = 0; k < j; k++) {
if (judge(i,j,i,k)) {
cpy = true;
}
}
}
}
int result = xmove * b + ymove * a;
if (cpx && cpy) {
result += mini(a,b);
}
cout<<result<<endl;
return 0;
}