【Codeforces gym102268A Angle Beats】【帶花樹算法】

題意

給一個nmn*m的網格,其中有“*”和“.”和“+”,每次可以選擇覆蓋一個"+“或“*”和與它相鄰的兩個”.",如果選的是“*”則兩個“.”必須相對。每個點只能被覆蓋一次,問最多能覆蓋多少次。
n,m100n,m\le 100

分析

把每個“+”和“*”拆成兩個點並連邊,“+”的每個點對四周的“.”連邊,“*”則一個點向上下方向連邊,另一個點向左右方向連邊,可以發現最大圖匹配減去“+”和“*”的數量即爲答案。

代碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

const int N=20005;
const int L=105;

int n,m,cnt,last[N],ty[N],tic[N],tim,f[N],match[N],pre[N];
int tot,dx[4]={0,1,0,-1},dy[4]={1,0,-1,0},id1[L][L],id2[L][L],sz;
bool used[26];
struct edge{int to,next;}e[N*10];
queue<int> que;
char str[L][L];

void addedge(int u,int v)
{
    e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
    e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}

int find(int x)
{
    if (f[x]==x) return x;
    else return f[x]=find(f[x]);
}

int lca(int x,int y)
{
    for (tim++;;swap(x,y))
        if (x)
        {
            x=find(x);
            if (tic[x]==tim) return x;
            tic[x]=tim;x=pre[match[x]];
        }
}

void shrink(int x,int y,int p)
{
    while (find(x)!=p)
    {
        pre[x]=y;y=match[x];
        if (ty[y]==2) ty[y]=1,que.push(y);
        if (find(x)==x) f[x]=p;
        if (find(y)==y) f[y]=p;
        x=pre[y];
    }
}

bool aug(int s)
{
    for (int i=1;i<=sz;i++) f[i]=i,ty[i]=pre[i]=0;
    while (!que.empty()) que.pop();
    que.push(s);ty[s]=1;
    while (!que.empty())
    {
        int x=que.front();que.pop();
        for (int i=last[x],y=e[i].to;i;i=e[i].next,y=e[i].to)
        {
            if (find(x)==find(y)||ty[y]==2) continue;
            if (!ty[y])
            {
                ty[y]=2;pre[y]=x;
                if (!match[y])
                {
                    for (int tmp;y;y=tmp,x=pre[y])
                        tmp=match[x],match[x]=y,match[y]=x;
                    return 1;
                }
                else ty[match[y]]=1,que.push(match[y]);
            }
            else if (ty[y]==1)
            {
                int p=lca(x,y);
                shrink(x,y,p);
                shrink(y,x,p);
            }
        }
    }
    return 0;
}

void build()
{
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			if (str[i][j]=='.') id1[i][j]=++sz;
			else id1[i][j]=++sz,id2[i][j]=++sz,addedge(id1[i][j],id2[i][j]);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			if (str[i][j]=='*')
			{
				if (str[i][j-1]=='.') addedge(id1[i][j],id1[i][j-1]);
				if (str[i][j+1]=='.') addedge(id1[i][j],id1[i][j+1]);
				if (str[i-1][j]=='.') addedge(id2[i][j],id1[i-1][j]);
				if (str[i+1][j]=='.') addedge(id2[i][j],id1[i+1][j]);
			}
			else if (str[i][j]=='+')
			{
				for (int k=0;k<4;k++)
				{
					int x=i+dx[k],y=j+dy[k];
					if (str[x][y]=='.') addedge(id1[i][j],id1[x][y]),addedge(id2[i][j],id1[x][y]);
				}
			}
}

void block(int x,int y)
{
	for (int k=0;k<4;k++)
	{
		int p=x+dx[k],q=y+dy[k];
		if (str[p][q]>='a'&&str[p][q]<='z') used[str[p][q]-'a']=1;
	}
}

char filter(int x,int y)
{
	memset(used,0,sizeof(used));
	block(x,y);
	for (int k=0;k<4;k++)
	{
		int p=x+dx[k],q=y+dy[k];
		if (id1[p][q]==match[id1[x][y]]||id1[p][q]==match[id2[x][y]]) block(p,q);
	}
	for (int i=0;i<26;i++)
		if (!used[i]) return i+'a';
}

void output()
{
	for (int i=1;i<=n;i++)
    	for (int j=1;j<=m;j++)
    		if (str[i][j]=='*'||str[i][j]=='+')
    		{
    			if (match[id1[i][j]]==id2[i][j]||!match[id1[i][j]]||!match[id2[i][j]]) continue;
    			char c=filter(i,j);
    			str[i][j]=c;
    			for (int k=0;k<4;k++)
    			{
    				int x=i+dx[k],y=j+dy[k];
    				if (match[id1[x][y]]==id1[i][j]||match[id1[x][y]]==id2[i][j]) str[x][y]=c;
    			}
    		}
    for (int i=1;i<=n;i++)
    {
    	for (int j=1;j<=m;j++)
    		printf("%c",str[i][j]);
    	puts("");
    }
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%s",str[i]+1);
	build();
    int ans=0;
    for (int i=1;i<=sz;i++) if (!match[i]&&aug(i)) ans++;
    output();
    return 0;
}

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