1. 程式人生 > >Uva 10615 Rooks(二分圖完美匹配+補邊)

Uva 10615 Rooks(二分圖完美匹配+補邊)

思路:

1.給棋子染色,要求每行每列棋子顏色均不相同。

2.構造二分圖模型,左右各n個點,若第i行第j列存在棋子,則由左邊第i個點向右邊第j個點連邊。則問題變為給邊染色,使得每個點所連邊顏色均不相同。

3.對於二分圖中每個點,若存在一完美匹配,則所有匹配邊無衝突,可染同一色,設需要使用ans種顏色,則可求ans次完美匹配。

4.由於每點度數可能不相同,完美匹配不一定存在,若最大匹配不是完美匹配,則有可能造成ans次匹配後存在邊為染色,所以應新增多餘的邊,使得每次求出的匹配為最大匹配。

5.設所有點的最大度數為k,則所需顏色數至少為k。

5.若左邊一點的所連邊數小於k,則尋找右邊所連邊數小於k的點,連邊,直到左邊所有點所連邊數等於k。則該二分圖為k階正則二分圖(每點的度數均為k),由定理可知k階正則二分圖必存在完美匹配,則找到該組匹配後,從圖中刪去匹配邊,二分圖變為k-1階正則二分圖,一直處理,知道所有邊都匹配為止。由此,ans=k。補邊後求k次完美匹配即可。

#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug
using namespace std;
const int maxn=100+50;

int n,ans;
int vis[maxn];
char ch[maxn][maxn];
vector<int> g[maxn];
int ansG[maxn][maxn];
int rt[maxn],lt[maxn];
int cnt1[maxn],cnt2[maxn];

int match(int u)
{
    for(int i=0; i<g[u].size(); i++)
    {
        int nt=g[u][i];
        if(!vis[nt])
        {
            vis[nt]=1;
            if(rt[nt]==-1||match(rt[nt]))
            {
                rt[nt]=u;
                lt[u]=nt;
                return 1;
            }
        }
    }
    return 0;
}

void hungary()
{
    memset(rt,-1,sizeof(rt));
    for(int i=1; i<=n; i++)
    {
        memset(vis,0,sizeof(vis));
        match(i);
    }
}

void init()
{
    ans=0;
    memset(cnt1,0,sizeof(cnt1));
    memset(cnt2,0,sizeof(cnt2));
    memset(ansG,0,sizeof(ansG));
    for(int i=0; i<=n; i++) g[i].clear();
}

void solve()
{
    for(int i=1; i<=ans; i++)
    {
        hungary();
        for(int j=1; j<=n; j++)
        {
            if(ch[j][lt[j]]=='*')
            {
                //cout<<j<<" "<<lt[j]<<endl;
                ansG[j][lt[j]]=i;
            }
            for(int k=0; k<g[j].size(); k++)
            {
                if(g[j][k]==lt[j])
                {
                   // cout<<"* "<<j<<" "<<lt[j]<<" "<<g[j][k]<<endl;
                    g[j].erase(g[j].begin()+k);
                    break;
                }
            }
        }
    }
}

void build()
{
    for(int i=1; i<=n; i++)
    {
        ans=max(ans,max(cnt1[i],cnt2[i]));
    }

    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n&&cnt1[i]<ans; j++)
        {
            while(cnt1[i]<ans&&cnt2[j]<ans)
            {
                cnt1[i]++,cnt2[j]++;
                g[i].push_back(j);
            }
        }
    }
}

int main()
{
#ifdef debu
    freopen("in.txt","r",stdin);
#endif // debug

    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        init();
        for(int i=1; i<=n; i++)
        {
            getchar();
            for(int j=1; j<=n; j++)
            {
                scanf("%c",&ch[i][j]);
                if(ch[i][j]=='*')
                {
                    cnt1[i]++,cnt2[j]++;
                    g[i].push_back(j);
                }
            }
        }

        build();
        solve();

        printf("%d\n",ans);
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                if(j==1) printf("%d",ansG[i][j]);
                else printf(" %d",ansG[i][j]);
            }
            printf("\n");
        }
    }
    return 0;
}