Luogu P3965 [TJOI2013] 循环格 题解 [ 紫 ] [ 费用流建模 ]

循环格:简单费用流,5min 秒了。

观察

考虑循环格的性质,不难发现我们可以把每个格子看做一个节点,那么每个节点便都有一个出度指向其他节点。这个就很像内向基环树的性质啊,如果循环格合法(从每个点出发能回到自己),那么就得满足每个点都在一个环中。问题就转化为我们要最小化操作次数,使得所有点都在一个环里。

继续考虑环的性质,显然形成环是需要每个点入度都是 \(1\),出度也都是 \(1\)。出度的限制已经满足了,我们只要让每个点的入度都是 \(1\) 即可。这应该是一个很典的 trick 了吧。

建模

到这里就是好做的了。你发现把每个点拆成入点和出点,把每个节点能旋转到的四个方向分别看做容量为 \(1\) 的边,不用旋转的方向费用为 \(0\),其余三个方向费用为 \(1\),跑最小费用最大流即可。本质上就是个二分图匹配。

时间复杂度为 EK 或者 Dinic 的复杂度。

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
const int N=1005,M=1000005;
const ll inf=0x3f3f3f3f3f3f3f3f;
int n,m,s,t;
void getpos(int &x,int &y,int twd)
{
    if(twd==0)x+=1;
    else if(twd==1)x-=1;
    else if(twd==2)y+=1;
    else if(twd==3)y-=1;
    if(x==0)x=n;
    else if(x==n+1)x=1;
    if(y==0)y=m;
    else if(y==m+1)y=1;
}
int getid(int x,int y,int tp)
{
    return ((x-1)*m+y+tp*n*m);
}
int h[N],cur[N],idx=1;
struct Edge{
    int v,ne;
    ll c,w;
}e[M];
void add(int u,int v,ll c,ll w)
{
    e[++idx]={v,h[u],c,w};
    h[u]=idx;
}
void addeg(int u,int v,ll c,ll w)
{
    add(u,v,c,w);
    add(v,u,0,-w);
}
ll dis[N],cost;
bitset<N>vis;
bool SPFA()
{
    memset(dis,0x3f,sizeof(dis));
    vis.reset();
    queue<int>q;
    dis[s]=0;
    q.push(s);
    vis[s]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=h[u];i;i=e[i].ne)
        {
            int v=e[i].v;ll c=e[i].c,w=e[i].w;
            if(dis[v]>dis[u]+w&&c)
            {
                dis[v]=dis[u]+w;
                if(vis[v]==0)
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    return (dis[t]<=inf/2);
}
ll dfs(int u,ll mf)
{
    if(u==t)return mf;
    ll sm=0;
    vis[u]=1;
    for(int i=cur[u];i;i=e[i].ne)
    {
        int v=e[i].v;ll c=e[i].c,w=e[i].w;
        if(vis[v]==0&&dis[v]==dis[u]+w&&c)
        {
            ll res=dfs(v,min(c,mf));
            cost+=w*res;
            e[i].c-=res;
            e[i^1].c+=res;
            mf-=res;
            sm+=res;
            if(mf==0)break;
        }
    }
    if(sm==0)dis[u]=inf;
    return sm;
}
void dinic()
{
    ll flow=0;
    while(SPFA())
    {
        memcpy(cur,h,sizeof(h));
        vis.reset();
        flow+=dfs(s,inf);
    }
}
int main()
{
    //freopen("sample.in","r",stdin);
    //freopen("sample.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    s=n*m*2+1,t=n*m*2+2;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            char c;
            cin>>c;
            for(int k=0;k<4;k++)
            {
                int w=1,tx=i,ty=j;
                getpos(tx,ty,k);
                if(c=='D'&&k==0)w=0;
                if(c=='U'&&k==1)w=0;
                if(c=='R'&&k==2)w=0;
                if(c=='L'&&k==3)w=0;  
                addeg(getid(i,j,0),getid(tx,ty,1),1,w);              
            }
            addeg(s,getid(i,j,0),1,0);
            addeg(getid(i,j,1),t,1,0);
        }
    }
    dinic();
    cout<<cost;
    return 0;
}
posted @ 2025-04-20 17:12  KS_Fszha  阅读(13)  评论(0)    收藏  举报