1. 程式人生 > >[BZOJ3171/Luogu3965][TJOI2013]迴圈格

[BZOJ3171/Luogu3965][TJOI2013]迴圈格

題目連結:

BZOJ3171

Luogu3965

首先,有一個很顯然的結論,每一個格點都要有且只有一條入邊,因為整張圖有\(r*c\)個點和邊,每個點都要有入邊。

現在要平均分配每一條邊,次數最小,那麼就很簡單了,費用流。

把每個點拆成入點和出點,對於每個入點,和源點連邊,容量\(1\),費用\(0\),表示可以向其他點貢獻入邊。

對於每個點向四周連邊,容量為\(1\)(可以向四周連邊),若方向與箭頭相同費用為\(0\)(不需要改變),否則為\(1\)(需要修改)。

對於每個出點向匯點連邊,容量\(1\),費用\(0\)(得到了一條入邊)。

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ID(x,y,z) ((z)*n*m+((x)-1)*m+(y))

inline int Min(int a,int b){return a<b?a:b;}
inline int Max(int a,int b){return a>b?a:b;}

int n,m,St,Ed,MaxFlow,MinCost;
int Head[505],Next[6005],To[6005],Val[6005],Cos[6005],En=1;
int Pre[505],Ref[505],Dis[505];
char Grid[20];
bool Inq[505];
const int nx[]={-1,1,0,0},ny[]={0,0,-1,1};
const char s[]={'U','D','L','R'};

inline void Add(int x,int y,int z,int w)
{
    Next[++En]=Head[x],To[Head[x]=En]=y,Val[En]=z,Cos[En]=+w;
    Next[++En]=Head[y],To[Head[y]=En]=x,Val[En]=0,Cos[En]=-w;
}

bool SPFA()//壓行費用流,不要在意
{
    std::queue<int> q;
    memset(Dis,0x3f,sizeof Dis);
    q.push(St),Dis[St]=0,Ref[St]=1<<30;
    for(int x,y;!q.empty();q.pop(),Inq[x]=false)
        for(int i=Head[x=q.front()];i;i=Next[i])
            if(Val[i]&&Dis[y=To[i]]>Dis[x]+Cos[i])
            {
                Dis[y]=Dis[x]+Cos[Pre[y]=i];
                Ref[y]=Min(Ref[x],Val[i]);
                if(!Inq[y])q.push(y),Inq[y]=true;
            }
    if(Dis[Ed]==0x3f3f3f3f)return false;
    MaxFlow+=Ref[Ed],MinCost+=Ref[Ed]*Dis[Ed];
    for(int x=Ed,i;x!=St;x=To[i^1])
        Val[i=Pre[x]]-=Ref[Ed],Val[i^1]+=Ref[Ed];
    return true;
}

int main()
{
    scanf("%d%d",&n,&m),St=n*m<<1|1,Ed=St+1;
    for(int i=1;i<=n;++i)
    {
        scanf("%s",Grid+1);
        for(int j=1;j<=m;++j)
        {
            int t=std::find(s,s+4,Grid[j])-s;
            Add(St,ID(i,j,0),1,0);
            Add(ID(i,j,1),Ed,1,0);//向源點與匯點連邊
            for(int k=0;k<4;++k)
            {
                int wx=i+nx[k],wy=j+ny[k];//箭頭方向
                if(!wx)wx=n;
                else if(wx>n)wx=1;
                if(!wy)wy=m;
                else if(wy>m)wy=1;//邊界處理
                Add(ID(i,j,0),ID(wx,wy,1),1,k!=t);
            }
        }
    }
    while(SPFA());
    printf("%d\n",MinCost);
    return 0;
}