The Coronation
題意:
給定個長度爲的串,定義兩個串相似:兩個串對應位置相同的位置數量不小於某給定值;可以通過反轉字符串使得兩個串從不相似變成相似,求最少的反轉次數使得所有的串兩兩相似。
思路:
- 剛看到這題時還想過網絡流、雙向bfs?QAQ,後面突然想到好像可以2-Sat,於是就A了。。。
- 那怎樣想到2-Sat呢?考慮到每個串有兩種狀態,只能選其中一個;並且這些串的狀態受到其他串的限制,很像2-Sat!
- 2-Sat關鍵就是從“不能怎樣”->“一定要那樣”,也就是從模糊的條件推出必須滿足的有向邊。而這裏先預處理出所有這樣的關係;對於任意兩個串處理出都不反轉和一個反轉這兩種情況是否相似,若都不相似,說明這對串沒救了!直接退出程序輸出;若兩種情況都滿足相似,則說明這兩個串直接沒有任何限制,不連邊;剩下的就是隻有一種情況相似,根據情況連雙向邊即可(爲什麼是雙向邊呢?其實是兩條單向邊的疊加)。
- 現在最關鍵的問題來了,如何使得反轉的數量最少呢?考慮到前面連邊的方式(雙向邊)導致不同的GCC之間沒有任何邊,也就是沒有任何限制。因此對於每個沒有確定的字符串,先隨便確定它的狀態進行,若失敗了,說明反轉後也會失敗!(這點很有趣)然後就可以直接退出程序輸出了。那如果成功了呢?怎樣保證反轉次數最少呢?單獨考慮當前GCC,GCC的整體(包含的所有字符串)都反轉,則當前GCC依然合法(畢竟都反轉等於都不反轉);因此若當前GCC中選擇反轉的數量大於其包含點數的,則選擇整體反轉,這樣就保證了反轉的數量儘可能小啦。
代碼
#include "bits/stdc++.h"
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {int x=0,f=1;char c=getchar();while(c!='-'&&(c<'0'||c>'9'))c=getchar();if(c=='-')f=-1,c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return f*x;}
const int maxn = 100+10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const double eps = 1e-7;
int n, m, k;
string s[maxn];
int head[maxn], to[maxn*maxn], nxt[maxn*maxn], tot;
int tmp[maxn], top;
bool choose[maxn];
inline void add_edge(int u, int v) {
++tot; to[tot]=v; nxt[tot]=head[u]; head[u]=tot;
++tot; to[tot]=u; nxt[tot]=head[v]; head[v]=tot;
}
bool dfs(int u) {
if(choose[u^1]) return 0;
if(choose[u]) return 1;
choose[u]=1, tmp[++top]=u;
for(int i=head[u]; i; i=nxt[i]) if(!dfs(to[i])) return 0;
return 1;
}
void solve() {
n=read(), m=read(), k=read();
for(int i=0; i<2*n; ++i) head[i]=choose[i]=0; tot=0;
for(int i=0; i<n; ++i) cin>>s[i];
for(int i=0; i<n; ++i) {
for(int j=i+1; j<n; ++j) {
int c1=0, c2=0;
for(int t=0; t<m; ++t) {
if(s[i][t]==s[j][t]) c1++;
if(s[i][t]==s[j][m-1-t]) c2++;
}
if(c1<k&&c2<k) return (void)printf("-1\n");
if(c1>=k&&c2>=k) continue;
else if(c1>=k) add_edge(2*i,2*j), add_edge(2*i+1,2*j+1);
else if(c2>=k) add_edge(2*i,2*j+1), add_edge(2*i+1,2*j);
}
}
vector<int> ans;
for(int i=0; i<n; ++i) {
if(choose[2*i]||choose[2*i+1]) continue;
top=0;
if(!dfs(2*i)) return (void)printf("-1\n");
int rev_num=0;
for(int j=1; j<=top; ++j) if(tmp[j]%2) rev_num++;
if(rev_num>top/2) {
for(int j=1; j<=top; ++j) if(tmp[j]%2==0) ans.push_back(tmp[j]/2+1);
}
else for(int j=1; j<=top; ++j) if(tmp[j]%2) ans.push_back(tmp[j]/2+1);
}
cout<<ans.size()<<endl;
for(auto p: ans) printf("%d ", p);
puts("");
}
int main() {
int T=read();
while(T--) solve();
}