題目鏈接:https://codeforces.com/contest/1250/problem/E
解題思路:
首先根據反轉的性質有:
有字符串a,b,a的反轉A,b的反轉B,如果a和b匹配值爲K,那麼A和B的值也爲K
同樣a和B匹配值爲M,那麼A和b的匹配值也爲M,因此有對稱性
那麼就可以轉換成2-sat問題了,設點i的對立點集是i+n,也就是a和A
如果i和j不管怎麼反轉都能匹配,那麼他們之間就沒有約束關係
如果i和j不管怎麼反轉都不匹配,那麼直接是無解的
如果i只能和j匹配,不能和j+n匹配,那麼聯合i和j,聯合i+n,j+n
如果只能和j+n匹配,那麼聯合i和j+n,聯合i+n,j
這個就可以用並查集維護,然後因爲我們需要更少的反轉,所以加上加權並查集,選擇權值較小的i集合或者i+n集合
#include<bits/stdc++.h>
using namespace std;
const int mx = 100+5;
const int mod = 1e9 + 7;
typedef long long ll;
int n,m,K;
int fa[mx],siz[mx],ans[mx];
char s[mx][mx];
vector <int> g[mx];
int find(int x) {
return x==fa[x]?x:fa[x]=find(fa[x]);
}
bool check(int u,int v) {
int cnt = 0;
for (int i=0;i<m;i++)
cnt += s[u][i] == s[v][i];
return cnt >= K;
}
bool solve() {
for (int i=0;i<n;i++) {
for (int j=i+1;j<n;j++) {
bool ok1 = check(i,j);
bool ok2 = check(i,j+n);
if (!ok1 && !ok2) return 0;
if (ok1 && ok2) continue;
int fi = find(i),fj = find(j);
int fi_n = find(i+n),fj_n = find(j+n);
if (ok2) swap(fj,fj_n);
if (fi == fj_n) return 0;
if (fi != fj) {
fa[fj] = fi;
siz[fi] += siz[fj];
fa[fj_n] = fi_n;
siz[fi_n] += siz[fj_n];
}
}
}
return 1;
}
int main(){
int t;
scanf("%d",&t);
while (t--) {
scanf("%d%d%d",&n,&m,&K);
for (int i=0;i<2*n;i++) {
fa[i] = i;
siz[i] = i >= n;
g[i].clear();
}
for (int i=0;i<n;i++) {
scanf("%s",s[i]);
reverse(s[i],s[i]+m);
memcpy(s[i+n],s[i],m+1);
reverse(s[i],s[i]+m);
}
bool ok = solve();
if (!ok) puts("-1");
else {
vector <int> ve;
int tot = 0;
for (int i=0;i<2*n;i++) {
int fi = find(i);
g[fi].push_back(i);
}
for (int i=0;i<n;i++) {
int fi = find(i);
int fi_n = find(i+n);
if (fi != i) continue;
if (siz[fi] <= siz[fi_n]) ve.push_back(fi);
else ve.push_back(fi_n);
}
for (int u : ve) {
for (int v :g[u])
if (v >= n) ans[tot++] = v - n + 1;
}
if (!tot) puts("0\n");
else {
printf("%d\n",tot);
for (int i=0;i<tot;i++)
printf("%d%c",ans[i],i==tot-1?'\n':' ');
}
}
}
return 0;
}