The Coronation(2019 ICPC Southern and Volga Russian Regional E題+ 2-Sat)

The Coronation

題意:

給定nn個長度爲mm0101串,定義兩個串相似:兩個串對應位置相同的位置數量不小於某給定值kk;可以通過反轉字符串使得兩個0101串從不相似變成相似,求最少的反轉次數使得所有的0101串兩兩相似。

思路:

  1. 剛看到這題時還想過網絡流、雙向bfs?QAQ,後面突然想到好像可以2-Sat,於是就A了。。。
  2. 那怎樣想到2-Sat呢?考慮到每個0101串有兩種狀態,只能選其中一個;並且這些0101串的狀態受到其他0101串的限制,很像2-Sat!
  3. 2-Sat關鍵就是從“不能怎樣”->“一定要那樣”,也就是從模糊的條件推出必須滿足的有向邊。而這裏先O(n2m)O(n^2*m)預處理出所有這樣的關係;對於任意兩個0101串處理出都不反轉和一個反轉這兩種情況是否相似,若都不相似,說明這對0101串沒救了!直接退出程序輸出1-1;若兩種情況都滿足相似,則說明這兩個0101串直接沒有任何限制,不連邊;剩下的就是隻有一種情況相似,根據情況連雙向邊即可(爲什麼是雙向邊呢?其實是兩條單向邊的疊加)。
  4. 現在最關鍵的問題來了,如何使得反轉的0101數量最少呢?考慮到前面連邊的方式(雙向邊)導致不同的GCC之間沒有任何邊,也就是沒有任何限制。因此對於每個沒有確定的字符串,先隨便確定它的狀態進行dfsdfs,若失敗了,說明反轉後也會失敗!(這點很有趣)然後就可以直接退出程序輸出1-1了。那如果成功了呢?怎樣保證反轉次數最少呢?單獨考慮當前GCC,GCC的整體(包含的所有字符串)都反轉,則當前GCC依然合法(畢竟都反轉等於都不反轉);因此若當前GCC中選擇反轉的數量大於其包含點數的12\frac{1}{2},則選擇整體反轉,這樣就保證了反轉的數量儘可能小啦。

代碼

#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();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章