ZZULIOJ 1875 蛤玮的财宝【最大费用流】

1875: 蛤玮的财宝

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 599   Solved: 138

Submit Status Web Board

Description

蛤玮和他的妹子出海游玩,不小心遭遇了海难,他们醒来之后发现自己到了一座金银岛.岛主非常好心的告诉他们在岛的另一边有船可以送他们回家.
这座岛可以看成n*m的矩阵,蛤玮他们在位置(1,1),而船在位置(n,m).蛤玮发现金银岛遍地都是金子,每个格子里有价值a[i,j]的金子,他和妹子打算在回去的路上带一些走.如果他们路过了位置(i,j),就可以假装系鞋带捡走地上的金子.为了不引起怀疑,他们在走的时候只能往接近码头的方向走,即如果蛤玮现在在(i,j),他只能移动到(i+1,j)或者(i,j+1).为了能拿走更多的金子,蛤玮和妹子决定装作互相不认识,这样他们就可以分开走,从而拿到更多的金子.
蛤玮和他妹子想知道他们最多能拿走多少金子.
注意如果蛤玮和他妹子经过了相同的地方,只能得到一次金子,因为地上的捡完就没有了.

Input

T(1<=T<=10),表示数据组数.
每组数据第一行n,m(1<=n,m<=100),接下来n行,每行m个数,第i行第j列的值a[i,j](1<=a[i,j]<=1000)表示位置(i,j)的金子的价值.

Output

每组数据输出一行,蛤玮和他妹子能拿到的金子总价的最大值.

Sample Input

1
2 2
2 1
1 2

Sample Output

6

思路:


很明显,要跑两次最长路,而且相互影响。

那么直接贪心的跑两次最长路肯定是不行的。

那么考虑最小费用流。


建图:


①首先将每个点一分为二,然后之间连一条费用为其权值.流为1的边。

②然后将源点连入起点,费用为0.流为2的边。

③然后将终点连入汇点,费用为0.流为2的边。

④然后将每两个相邻点相连,费用为0.流为INF。


之后跑最大费用流.


注意这题1*n这种数据,那么不必要跑满流。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<climits>
#include<iostream>
using namespace std;
#define INF 1<<28
struct node
{
    int from;
    int to;
    int w;
    int f;
    int num;
    int next;
} e[(610*610+2)+10];
int pre[610*610+2];
int path[610*6100+2];
int vis[610*610+2];
int dis[610*610+2];
int head[610*610+2];
int que[610*610+2];
int a[150][150];
int fx[2]= {1,0};
int fy[2]= {0,1};
int ss,tt,cont,n,m;
void add(int from,int to,int w,int f)
{
    e[cont].to=to;
    e[cont].from=from;
    e[cont].w=w;
    e[cont].f=f;
    e[cont].num=cont;
    e[cont].next=head[from];
    head[from]=cont++;
}
void init()
{
    memset(e,0,sizeof(e));
    memset(pre,-1,sizeof(pre));
    memset(path,-1,sizeof(path));
    memset(vis,0,sizeof(vis));
    memset(dis,0,sizeof(dis));
    memset(que,0,sizeof(que));
}
void getmap()
{
    ss=n*m*2+1;
    tt=ss+1;
    cont=0;
    memset(head,-1,sizeof(head));
    add(ss,1,0,2);
    add(1,ss,0,0);
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            if(i==1&&j==1||i==n&&j==m)
            {
                add((i-1)*m+j,(i-1)*m+j+n*m,a[i][j],2);
                add((i-1)*m+j+n*m,(i-1)*m+j,-a[i][j],0);
            }
            else
            {
                add((i-1)*m+j,(i-1)*m+j+n*m,a[i][j],1);
                add((i-1)*m+j+n*m,(i-1)*m+j,-a[i][j],0);
            }
        }
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            for(int k=0; k<2; k++)
            {
                int x=i+fx[k];
                int y=j+fy[k];
                if(x>=1&&x<=n&&y>=1&&y<=m)
                {
                    int u=(i-1)*m+j;
                    int uu=u+n*m;
                    int v=(x-1)*m+y;
                    int vv=v+n*m;
                    add(uu,v,0,1);
                    add(v,uu,0,0);
                }
            }
        }
    }
    add(n*m*2,tt,0,2);
    add(tt,n*m*2,0,0);
}
int SPFA()
{
    memset(que,0,sizeof(que));
    memset(vis,0,sizeof(vis));
    memset(path,-1,sizeof(path));
    memset(pre,-1,sizeof(pre));
    for(int i=1;i<=tt;i++)dis[i]=-INF;
    dis[ss]=0;
    vis[ss]=1;
    int tot=0;
    int rear=0;
    que[rear++]=ss;
    while(rear>tot)
    {
        int u=que[tot];
        tot++;
        vis[u]=0;
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int w=e[i].w;
            int f=e[i].f;
            int v=e[i].to;
            if(f>0&&dis[v]<dis[u]+w)
            {
                pre[v]=u;
                path[v]=e[i].num;
                dis[v]=dis[u]+w;
                if(vis[v]==0)
                {
                    vis[v]=1;
                    que[rear++]=v;
                }
            }
        }
    }
    if(dis[tt]!=-INF)return 1;
    else return 0;
}
void Max_costflow()
{
    int ans=0,maxflow=0;
    while(SPFA()==1)
    {
        int minn=INT_MAX;
        for(int i=tt;i!=ss;i=pre[i])
        {
            minn=min(minn,e[path[i]].f);
        }
        for(int i=tt;i!=ss;i=pre[i])
        {
            e[path[i]].f-=minn;
            e[path[i]^1].f+=minn;
        }
        maxflow+=minn;
        ans+=dis[tt]*minn;
    }
    if(maxflow==2)
    printf("%d\n",ans-a[1][1]-a[n][m]);
    else printf("%d\n",ans);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d%d",&n,&m);
        int sum=0;
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                scanf("%d",&a[i][j]);
                sum+=a[i][j];
            }
        }
        getmap();
        Max_costflow();
    }
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值