现在的位置: 首页 > 算法 > 正文

poj 3422 Kaka’s Matrix Travels (最小费用最大流 模板)

2018年09月21日 算法 ⁄ 共 2219字 ⁄ 字号 评论关闭

题目链接:   poj 3422

题目大意:   给出NxN的数字矩阵,左上角走到右下角(只能向右或向下走),sum记录走过点值的和

                  走过的点值置为0,问走K次,求sum的最大值

解题思路:   开始想到的是K次最长路,结果WA了

                  因为每次走最长路会导致下次走的时候不是最优解

                  建立费用流模型,把每个顶点T,分成两个顶点T和T''

                  因为要限制每个顶点的值只加一次,T->T''容量为1,费用为T的值:

                  1.建立超级源点,源点指向顶点T1,容量为K,费用0

                  2.建立超级汇点,顶点T(2*n)指向汇点,容量为K,费用为0

                  3.每个顶点拆分为两个点,加两条边 (Ti指向Ti'',容量为1,费用为T的值)

                     (Ti指向Ti'',容量为K,费用为0)

                  4.若能从T1走到T2,则T1''连T2,容量为K,费用为0

                  然后求一次最小费用最大流就是最终结果

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 5100
#define INF 0x3f3f3f3f
#define Min(a,b) (a<b?a:b)
int S,E,vis[MAX],pre[MAX],Index,dis[MAX],path[MAX],que[2000000],Fi[MAX*100];
int Fx[2][2]={{1,0},{0,1}};

struct snode{
    int r,to,c,w,next;
}Edge[MAX*100];

void Add_edge(int a,int b,int c,int d)
{
    d=-d;       //等价于负边求最小费用流
    Edge[Index].to=b,Edge[Index].c=c,Edge[Index].w=d;
    Edge[Index].next=pre[a],Edge[Index].r=Index+1,pre[a]=Index++;

    Edge[Index].to=a,Edge[Index].c=0,Edge[Index].w=-d;            //反向边C为0,费用为-d
    Edge[Index].next=pre[b],Edge[Index].r=Index-1,pre[b]=Index++;
}

bool BFS()
{
    int i,s,e,v1,v2;
    memset(vis,0,sizeof(vis));
    memset(path,-1,sizeof(path));
    memset(dis,INF,sizeof(dis));
    s=e=0;
    que[s++]=S; vis[S]=1; dis[S]=0;
    while(s!=e)
    {
        v1=que[e++];
        vis[v1]=0;
        for(i=pre[v1];i!=-1;i=Edge[i].next)
        {
            v2=Edge[i].to;
            if(Edge[i].c&&(dis[v2]==INF||dis[v2]>dis[v1]+Edge[i].w))  //<是最大费用流,>是最小费用流
            {
                dis[v2]=dis[v1]+Edge[i].w;
                path[v2]=v1,Fi[v2]=i;
                if(!vis[v2])
                {
                    vis[v2]=1;
                    que[s++]=v2;
                }
            }
        }
    }
    if(dis[E]==INF)
        return false;
    return true;
}

int Find()
{
    int i,sum=0,MIN=INF;
    for(i=E;i!=-1;i=path[i])
    {
        if(path[i]!=-1)
            MIN=Min(MIN,Edge[Fi[i]].c);
    }
    for(i=E;i!=-1;i=path[i])
    {
        if(path[i]!=-1)
        {
            Edge[Fi[i]].c-=MIN;
            Edge[Edge[Fi[i]].r].c+=MIN;
            sum=sum+(Edge[Fi[i]].w*MIN);
        }
    }
    return sum;
}

int Solve()
{
    int sum=0;
    while(BFS())
        sum+=Find();
    return sum;
}

int main()
{
    int i,j,n,k,a,b,c,d,num[53][53];
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        Index=0;
        memset(pre,-1,sizeof(pre));
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                scanf("%d",&num[i][j]);
        S=0,E=n*n*2+1;
        Add_edge(S,1,k,0);    //左边
        Add_edge(E-1,E,k,0);  //右边
        int v1,v2,j1,x,y;
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            {
                v1=(i-1)*n+j;
                Add_edge(v1*2-1,v1*2,1,num[i][j]);   //分点边1
                Add_edge(v1*2-1,v1*2,k,0);           //分点边2
                for(j1=0;j1<2;j1++)
                {
                    x=i+Fx[j1][0],y=j+Fx[j1][1];
                    if(x>=1&&x<=n&&y>=1&&y<=n)
                    {
                        v2=(x-1)*n+y;
                        Add_edge(v1*2,v2*2-1,k,0);   //各点间
                    }
                }
            }
        }
        printf("%d\n",-Solve());
    }
    return 0;
}

抱歉!评论已关闭.