題目鏈接:點擊打開鏈接
題目大意:給一個無向圖(1<n<=500),每個點爲黑色或者是白色,要求相鄰的節點顏色不相同。現在有一種操作,交換兩個相鄰節點的顏色。問使得圖顏色合法的最少操作數,並輸出操作序列,如果不可能讓圖合法則輸出-1。
解析:
當一個圖有解時,確定任意一個節點的顏色,則其他節點的顏色均能確定,根據距離奇偶判定即可,那麼一定有兩種染色方案。
下面兩種情況是不可能得到合法的染色的:
1.圖中存在奇環。
2.錯誤的黑點個數不等於白點個數。所謂的錯誤的顏色就是指該位置本應爲白(黑)色,實際爲黑(白)色。
奇環可以通過一遍dfs判斷。
如果兩種染色方案的錯誤黑點個數均不等於錯誤白點個數,說明不可行,這也可以通過dfs判斷。
要交換兩個位置的點的顏色,並不產生其他影響,交換的最少操作數爲兩點在圖上的最短路徑。首先要交換的兩個點顏色一定不相同,從某一點觸發,需要和路徑上的異色點交換,同色點不需要交換。再從另一端點返回,同樣也是與異色點交換。
例如
1234567
0001011
需要將2和7位置互換。
交換序列爲:
(3,4)(5,6)(6,7)(5,4)(3,2)
1234567
0101010
最短距離可以bfs得到,尋找點到點長度爲k的最短路徑可以暴力dfs找。剩下來的問題就是如何對錯誤的點進行交換,交換代價爲最短路徑,只需要跑一個帶權匹配就可以了。注意,找到一個匹配的時候,需要兩個端點染色更新,但不能直接更新到最終的染色中,因爲有兩種染色方案,所以需要比較兩種方案,取更優的更新答案。
因爲圖可能不連通,所以對於每個連通塊做一遍上述操作即可。
細節比較多...
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <bitset>
using namespace std;
typedef long long ll;
const int mod = 1000000007;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll INF = 100000000000000000ll;
const int MAXN = 505;
const int MAXM = 310000;
namespace MCMF{
const static int N = 505;
const static int M = 310000;
int head[N], pre[N], cur[N], d[N], vis[N], s, e, no;
struct point{
int u, v, flow, next,cost;
point(){};
point(int x, int y, int z, int w, int c):u(x), v(y), next(z), flow(w), cost(c){};
}p[M];
void add(int x, int y, int z, int c){
p[no] = point(x, y, head[x], z, c); head[x] = no++;
p[no] = point(y, x, head[y], 0, -c); head[y] = no++;
}
void init(){
memset(head, -1, sizeof(head));
no = 0;
}
bool spfa(){
int i, x, y;
queue<int>q;
memset(d, 0x3f, sizeof(d));
memset(vis, false, sizeof(vis));
memset(pre, -1, sizeof(pre));
d[s] = 0; vis[s] = true; q.push(s);
while(!q.empty()){
x = q.front(); q.pop(); vis[x] = false;
for(i = head[x]; i != -1; i = p[i].next){
if(p[i].flow && d[y = p[i].v] > d[x] + p[i].cost){
d[y] = d[x] + p[i].cost; pre[y] = i;
if(vis[y]) continue;
vis[y] = true; q.push(y);
} } }
return d[e] != d[e + 1];
}
int mcmf(){
int mincost = 0, maxflow = 0, minflow, i;
while(spfa()){
minflow =INF;
for(i = pre[e]; i != -1; i = pre[p[i].u])
minflow = min(minflow, p[i].flow);
for(i = pre[e]; i != -1; i = pre[p[i].u]){
p[i].flow -= minflow;
p[i ^ 1].flow += minflow;
}
mincost += d[e] * minflow; maxflow += minflow;
}
return mincost;
}
};
int n,m;
char str[505],ss[2][505];
int head[505],wr[505],id[505],dis[505],nn,b,w,tot,qz[505],iqz[505],vis[505],ans[2];//qz是圖到網絡流圖的節點映射,iqz是逆向映射
vector<pair<int,int> > temp[2],step;
vector<int> seq;
struct node{
int v;
int next;
node(int t,int n):v(t),next(n){}
node(){}
}edge[MAXM];
void init(){
nn = 0;
tot = 0;
memset(head,-1,sizeof(head));
memset(id,-1,sizeof(id));
step.clear();
}
bool judge(int x,int la){//判斷是否有奇環
bool res = true;
id[x] = la;
for(int i = head[x];~i;i = edge[i].next){
int v = edge[i].v;
if(id[v] == -1){
res&=judge(v,la+1);
if(!res) return false;
}
else{
if((la-id[v]+1)%2) return false;
}
}
return res;
}
void dfs(int s,int c){//找錯誤的顏色節點
vis[s] = id[s] = 1;
if(str[s]-'0'!= c) {
wr[s] = 1;
if(c){
w++;
iqz[++tot] = s;
qz[s] = tot;
}
else{
b++;
iqz[++tot] = s;
qz[s] = tot;
}
}
for(int i = head[s];~i;i = edge[i].next){
int v = edge[i].v;
if(!vis[v]){
dfs(v,c^1);
}
}
}
void bfs(int x){//計算最短距離
memset(dis,-1,sizeof(dis));
queue<int> q;
q.push(x);
dis[x] = 0;
while(!q.empty()){
x = q.front();
int d = dis[x];
q.pop();
for(int i = head[x];~i;i = edge[i].next){
int v = edge[i].v;
if(dis[v] == -1){
dis[v] = d+1;
q.push(v);
}
}
}
}
int findpath(int x,int to,int dep,int dis){//找最短路徑
if(dep>dis) return 0;
vis[x] = 1;
int res = 0;
if(x == to&&dep == dis){
res = 1;
}
for(int i = head[x];~i&&!res;i = edge[i].next){
int v = edge[i].v;
if(!vis[v]){
res |= findpath(v,to,dep+1,dis);
}
}
if(res){
seq.push_back(x);
}
vis[x] = 0;
return res;
}
int main()
{
//specialjudge sj;
//sj.judge();
//freopen("1007.in","r",stdin);
//freopen("1007.out","w",stdout);
int T;
int cas = 1;
cin>>T;
while(T--){
init();
//printf("CAS:%d\n",cas++);
scanf("%d%d%s",&n,&m,str+1);
for(int i = 0;i < m;i++){
int x,y;
scanf("%d%d",&x,&y);
edge[nn] = node(y,head[x]);
head[x] = nn++;
edge[nn] = node(x,head[y]);
head[y] = nn++;
}
bool flag = true;
for(int i = 1;i <= n;i++){
if(id[i] == -1){
flag = judge(i,1);
if(!flag) break;
}
}
if(!flag){
printf("-1\n");
continue;
}
memset(id,0,sizeof(id));
flag = true;
int res = 0;
for(int i = 1;i <= n;i++){//枚舉所有連通塊
if(!id[i]&&flag){
bool flag2 = false;
for(int l = 0;l < 2;l++){
memset(wr,0,sizeof(wr));
memset(vis,0,sizeof(vis));
for(int j = 1;j <= n;j++) ss[l][j] = str[j];
temp[l].clear();
b = 0;
w = 0;
tot = 0;
ans[l] = inf;
dfs(i,l);
if(w != b) {
continue;
}
else{
flag2 = true;
}
MCMF::init();
MCMF::s = 0;
MCMF::e = tot+1;
for(int j = 1;j <= n;j++){//網絡流建圖
if(id[j]&&wr[j]&&str[j]-'0'==1){
MCMF::add(MCMF::s,qz[j],1,0);
}
if(id[j]&&wr[j]&&str[j]-'0'==0){
MCMF::add(qz[j],MCMF::e,1,0);
}
}
for(int j = 1;j <= n;j++){
if(id[j]&&wr[j]&&str[j]-'0'==1){
bfs(j);
for(int k = 1;k <= n;k++){
if(id[k]&&wr[k]&&str[k]-'0'==0){
MCMF::add(qz[j],qz[k],1,dis[k]);
}
}
}
}
ans[l] = MCMF::mcmf();
for(int j = 1;j <= tot;j++){
int cc = iqz[j];
if(str[cc]-'0' == 0){//if it connect to end
for(int k = MCMF::head[j];~k;k = MCMF::p[k].next){
if(MCMF::p[k].v != MCMF::e && MCMF::p[k].flow == 1){
for(int o = 0;o < 505;o++) vis[o] = 0;
seq.clear();
int from,to,dis;
from = iqz[MCMF::p[k].v];
to = iqz[j];
dis = -MCMF::p[k].cost;
findpath(from,to,0,dis);
int c = ss[l][seq[0]]-'0',pos = 0;
for(int o = 0;o < 505;o++) vis[o] = 0;
for(int o = 1;o < seq.size();o++){//輸出操作
int tc = ss[l][seq[o]]-'0';
if(c^tc){
vis[o] = 1;
int a = seq[pos],b = seq[o];
temp[l].push_back(pair<int,int>(a,b));
swap(ss[l][a],ss[l][b]);
}
pos = o;
}
for(int o = seq.size()-1;o > 0;o--){//輸出操作
if(!vis[o]){
int a = seq[o],b = seq[o-1];
temp[l].push_back(pair<int,int>(a,b));
swap(ss[l][a],ss[l][b]);
}
}
}
}
}
}
}
if(!flag2){
flag = false;
break;
}
int op;
if(ans[0]<ans[1]){
op = 0;
}
else op = 1;
res += ans[op];
for(int j = 0;j < temp[op].size();j++){
step.push_back(temp[op][j]);
}
for(int j = 1;j <= n;j++) str[j] = ss[op][j];//更新狀態
}
}
if(!flag){
printf("-1\n");
continue;
}
printf("%d\n",res);
for(int i = 0;i < step.size();i++){
printf("%d %d\n",step[i].first,step[i].second);
}
}
return 0;
}