HDU5740 Glorious Brilliance

题目链接:点击打开链接

题目大意:给一个无向图(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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章