题目链接:点击打开链接
题目大意:给一个无向图(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;
}