HDU 4859(Bestcoder #1 1003)海岸线(网络流之最小割)

题目地址:HDU4859

做了做杭电多校,知识点会的太少了,还是将重点放在刷专题补知识点上吧,明年的多校才是重点。

这题题目求的最长周长,可以试想一下,这里的海岸线一定是在“.”和“D”之间的,也就是说求最多的相邻的“.”和“D”的配对对数。可以先转化成最小割求最小配对对数,因为总对数是一定的,只需要减去就行。

要先对周围填充上一圈的“D”,然后变成了一个(n+2)*(m+2)的矩形。因为要求的都是相邻的匹配数,所以可以利用黑白染色转化成一个二分图模型,对于每对相邻的进行加边。二分图已经分成了X集和Y集两个子集合,假设要把所有的“.”都要分到X集,所有的"D"都要分到Y集(仅仅是假设),这时候分两种情况讨论:

1:若当前点在地图上是 . 但是却被分到了Y集,或者当前点是 D ,却被分到了X集,就和源点连一条INF的边。

2:若当前点在地图上是 . 并且被分到了X集,或者当前点是 D ,被分到了Y集,就和汇点连一条INF的边。

分析一下就知道,只有 . --> . 或者 D --> D ,也就是类型相同的,才能从源流向汇。

源连接的点是“分错了”的点( X集的D或者Y集的. ),汇连接的点是“分对了”的点(X集的.或者Y集的D),所以如果要从源流到汇,则一定是D->D或者.->.,也就是相同类型的,求出最小割就是最小化相同的。

最后用总对数减去这个就是答案.

代码如下:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <queue>
#include <map>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int head[3000], source, sink, nv, cnt;
int cur[3000], num[3000], d[3000], pre[3000];
int jx[]={0,0,1,-1};
int jy[]={1,-1,0,0};
struct node
{
    int u, v, cap, next;
}edge[1000000];
void add(int u, int v, int cap)
{
    edge[cnt].v=v;
    edge[cnt].cap=cap;
    edge[cnt].next=head[u];
    head[u]=cnt++;

    edge[cnt].v=u;
    edge[cnt].cap=0;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}
void bfs()
{
    memset(d,-1,sizeof(d));
    memset(num,0,sizeof(num));
    queue<int>q;
    q.push(sink);
    d[sink]=0;
    num[0]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(d[v]==-1)
            {
                d[v]=d[u]+1;
                num[d[v]]++;
                q.push(v);
            }
        }
    }
}
int isap()
{
    memcpy(cur,head,sizeof(cur));
    bfs();
    int flow=0, u=pre[source]=source, i;
    while(d[source]<nv)
    {
        if(u==sink)
        {
            int f=INF, pos;
            for(i=source;i!=sink;i=edge[cur[i]].v)
            {
                if(f>edge[cur[i]].cap)
                {
                    f=edge[cur[i]].cap;
                    pos=i;
                }
            }
            for(i=source;i!=sink;i=edge[cur[i]].v)
            {
                edge[cur[i]].cap-=f;
                edge[cur[i]^1].cap+=f;
            }
            flow+=f;
            u=pos;
        }
        for(i=cur[u];i!=-1;i=edge[i].next)
        {
            if(d[edge[i].v]+1==d[u]&&edge[i].cap) break;
        }
        if(i!=-1)
        {
            cur[u]=i;
            pre[edge[i].v]=u;
            u=edge[i].v;
        }
        else
        {
            if(--num[d[u]]==0) break;
            int mind=nv;
            for(i=head[u];i!=-1;i=edge[i].next)
            {
                if(mind>d[edge[i].v]&&edge[i].cap)
                {
                    mind=d[edge[i].v];
                    cur[u]=i;
                }
            }
            d[u]=mind+1;
            num[d[u]]++;
            u=pre[u];
        }
    }
    return flow;
}
int main()
{
    int t, n, m, i, j, mp[60][60], num=0;
    char s[60];
    scanf("%d",&t);
    while(t--)
    {
        num++;
        scanf("%d%d",&n,&m);
        memset(mp,0,sizeof(mp));
        for(i=1;i<=n;i++)
        {
            scanf("%s",s);
            for(j=0;j<m;j++)
            {
                if(s[j]=='E')
                    mp[i][j+1]=2;
                else if(s[j]=='.')
                    mp[i][j+1]=1;
            }
        }
        cnt=0;
        memset(head,-1,sizeof(head));
        source=0;
        sink=(n+2)*(m+2)+1;
        nv=sink+1;
        for(i=0;i<=n+1;i++)
        {
            for(j=0;j<=m+1;j++)
            {
                if((i+j)%2==0)
                {
                    if(mp[i][j]==1)
                    {
                        add(i*(m+2)+j+1,sink,INF);
                    }
                    else if(mp[i][j]==0)
                    {
                        add(source,i*(m+2)+j+1,INF);
                    }
                }
                else
                {
                    if(mp[i][j]==0)
                    {
                        add(i*(m+2)+j+1,sink,INF);
                    }
                    else if(mp[i][j]==1)
                    {
                        add(source,i*(m+2)+j+1,INF);
                    }
                }
                for(int k=0;k<4;k++)
                {
                    int x=i+jx[k];
                    int y=j+jy[k];
                    if(x>=0&&x<=n+1&&y>=0&&y<=m+1)
                    {
                        add(i*(m+2)+j+1,x*(m+2)+y+1,1);
                    }
                }
            }
        }
        int ans;
        ans=isap();
        printf("Case %d: %d\n",num,(n+1)*(m+2)+(n+2)*(m+1)-ans);
    }
    return 0;
}



你可能感兴趣的:(编程,算法,C语言,HDU,网络流)