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

发布了1107 篇原创文章 · 获赞 148 · 访问量 45万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览