HDU3376 Matrix Again 最小费用最大流+拆点

题意

有一个矩阵,问从左上角走到右下角(只能往右或者往下走),再从右下角回到左上角(只能往上或者往左),每个点只能走一次(除了左上角和右下角),问所走的格子的最大总和是多少。

思路

因为是矩阵形式,所以用拆点的方法,同一个点拆成两个,容量为1,因为容量为1所以每个点只能流过一次。

因为除了源点和汇点可以走两次外,其他点只能走一次,所以左上角的源点和右下的汇点容量为2,表示可以流过两次。

其他点之间容量为1。

如何建边?

tot=-1,s=1,t=2*n*n;
memset(head,-1,sizeof(head));
for(int i=1; i<=n; i++)
{
    for(int j=1; j<=n; j++)
    {
        scanf("%d",&a[i][j]);
        add((i-1)*n+j, (i+n-1)*n+j,1,-a[i][j]);//cap,cost
        if(i!=n)
            add((i+n-1)*n+j,i*n+j,1,0);
        if(j!=n)
            add((i+n-1)*n+j,(i-1)*n+j+1,1,0);
    }
}
add(s,n*n+1,1,0);//源点到起点,容量2,费用0
add(n*n,t,1,0);//终点到汇点,容量2,费用0

剩下部分代码套板子就行。

注意

对于数组需要开多大,考虑一下,我错了十几次。

AC代码

#include<stdio.h>
#include<iostream>
#include<queue>
#include<string.h>
using namespace std;
#define inf 0x3f3f3f3f

const int N=660;
bool book[N*N*2];//无*2 TLE
int n,m,tot,s,t,a[N][N],pre[N*N*2],head[N*N*2],dist[N*N*2];//无*2 RT
struct node
{
    int v,nextt,cap,flow,cost;
} e[N*N*8];

void add(int u,int v,int cap,int cost)
{
    tot++;
    e[tot].v=v;
    e[tot].nextt=head[u];
    e[tot].cap=cap;
    e[tot].cost=cost;
    e[tot].flow=0;//
    head[u]=tot;

    tot++;
    e[tot].v=u;
    e[tot].nextt=head[v];
    e[tot].cap=0;
    e[tot].cost=-cost;
    e[tot].flow=0;//
    head[v]=tot;
}
//queue<int>Q;
bool SPFA()
{
    for(int i=0; i<=t; i++)
    {
        dist[i]=inf;
        book[i]=0;
        pre[i]=-1;//head不能放在这里i清,否则为0
    }
    queue<int>Q;
    while(!Q.empty())
        Q.pop();
    book[s]=1;
    dist[s]=0;
    Q.push(s);
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
        book[u]=0;
        for(int i=head[u]; i!=-1; i=e[i].nextt)
        {
            int v=e[i].v;
            if(e[i].cap>e[i].flow&&dist[v]>dist[u]+e[i].cost)
            {
                dist[v]=dist[u]+e[i].cost;
                pre[v]=i;
                if(book[v]==0)
                {
                    book[v]=1;
                    Q.push(v);
                }
            }
        }
    }
    if(dist[t]!=inf)
        return 1;
    return 0;
}

int MCMF()
{
    int flow=0,cost=0;
    while(SPFA())
    {
        int minn=inf;
        for(int i=pre[t]; i!=-1; i=pre[e[i^1].v])
            minn=min(minn,e[i].cap-e[i].flow);
        for(int i=pre[t]; i!=-1; i=pre[e[i^1].v])
        {
            e[i].flow+=minn;
            e[i^1].flow-=minn;
            cost+=e[i].cost*minn;
        }
        flow+=minn;
    }
    return cost;
}

int main()
{
    while(~scanf("%d",&n))
    {
        tot=-1,s=1,t=2*n*n;
        memset(head,-1,sizeof(head));
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                scanf("%d",&a[i][j]);
                add((i-1)*n+j, (i+n-1)*n+j,1,-a[i][j]);//cap,cost
                if(i!=n)
                    add((i+n-1)*n+j,i*n+j,1,0);
                if(j!=n)
                    add((i+n-1)*n+j,(i-1)*n+j+1,1,0);
            }
        }
        add(s,n*n+1,1,0);//源点到起点,容量2,费用0
        add(n*n,t,1,0);//终点到汇点,容量2,费用0
        int ans=MCMF();
        printf("%d\n",-ans);//最小费用最大流
    }
    return 0;
}
原文地址:https://www.cnblogs.com/OFSHK/p/12667765.html