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;
}


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