河南省第六届acm省赛 探寻宝藏&& poj 3422 (最大费用最大流)

本文介绍了一种利用最大费用最大流算法解决迷宫寻宝问题的方法。机器人在迷宫中行走两次,每次只能向右或向下移动,目标是收集最多的宝物。通过拆点建图和SPFA算法实现最优路径搜索。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

传说HMH大沙漠中有一个M*N迷宫,里面藏有许多宝物。某天,Dr.Kong找到了迷宫的地图,他发现迷宫内处处有宝物,最珍贵的宝物就藏在右下角,迷宫的进出口在左上角。当然,迷宫中的通路不是平坦的,到处都是陷阱。Dr.Kong决定让他的机器人卡多去探险。

但机器人卡多从左上角走到右下角时,只会向下走或者向右走。从右下角往回走到左上角时,只会向上走或者向左走,而且卡多不走回头路。(即:一个点最多经过一次)。当然卡多顺手也拿走沿路的每个宝物。

Dr.Kong希望他的机器人卡多尽量多地带出宝物。请你编写程序,帮助Dr.Kong计算一下,卡多最多能带出多少宝物。

输入

第一行: K     表示有多少组测试数据。 

接下来对每组测试数据:

1:       M   N

2~M+1行: Ai1  Ai2 ……AiN    (i=1,..,m)

2k5      1M, N50     0Aij100    (i=1,.,M; j=1,,N)

所有数据都是整数。 数据之间有一个空格。

输出

对于每组测试数据,输出一行:机器人卡多携带出最多价值的宝物数

样例输入

2
2 3
0 10 10
10 10 80
3 3
0 3 9
2 8 5
5 7 100

样例输出

120
134
题意:左上走到右下捡数字,走两次,求最大和
求最大费用最大流只需把费用变成负数,用spfa找到一条费用最大的路
还有就是要拆点建图,一个点拆成两个,其中一条边费用为对应数字,流量1,另一条边费用为0,流量1
然后设个超级源点和超级汇点,分别连接左上,右下
这个和poj3422有些类似,那个题是走k次,这个只走两次
拆点建图,建完的图,就是每个点拆为两个,1&1',s→1→→1’→2→→2' 然后1↓3→→3'→4→→4’
大概就这个意思,上下左右也要连起来
代码注释:                                   

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define N 5050
#define inf 0x7fffffff
int n,m,s,t,maxflow,o;
struct node
{
    int u,v,c,f,next;
} a[100000];
int visit[N];
int pre[N];
int head[N];
int dis[N];
int map[55][55];
void add(int u,int v,int c,int f) //c为费用,f流量
{
    a[o].u=u,a[o].v=v,a[o].c=c,a[o].f=f;
    a[o].next=head[u],head[u]=o++;
    a[o].u=v,a[o].v=u,a[o].c=-c,a[o].f=0;//建立反边
    a[o].next=head[v],head[v]=o++;
}
int spfa()
{
    int i,u,v;
    memset(visit,0,sizeof(visit));
    memset(pre,-1,sizeof(pre));
    for(i=0; i<=t; i++)
        dis[i]=inf;
    queue<int>q;
    while(!q.empty())
        q.pop();
    q.push(s);
    visit[s]=1;
    dis[s]=0;
    while(!q.empty())
    {
        u=q.front();
        q.pop();
        for(i=head[u]; i!=-1; i=a[i].next)
        {
            v=a[i].v;
            if(a[i].f>0&&dis[u]+a[i].c<dis[v])
            {
                dis[v]=dis[u]+a[i].c;
                pre[v]=i;
                if(!visit[v])
                {
                    visit[v]=1;
                    q.push(v);
                }
            }
        }
        visit[u]=0;
    }
    if(dis[t]==inf)
        return 0;
    return 1;
}
void Add()//修改残留网络
{
    int i;
    int mm=inf;
    for(i=pre[t]; a[i].u!=s; i=pre[a[i].u])//找到最小可增流
        mm=min(mm,a[i].f);
    for(i=pre[t]; a[i].u!=s; i=pre[a[i].u])
    {
        a[i].f-=mm;
        a[i^1].f+=mm;//修改
        maxflow+=mm*a[i].c;//加到maxflow中
    }
}
int main()
{
    int T,i,j,x;
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        o=0;
        maxflow=0;
        memset(head,-1,sizeof(head));
        for(i=1; i<=n; i++)
            for(j=1; j<=m; j++)
                cin>>map[i][j];
        for(i=1; i<=n; i++)
            for(j=1; j<=m; j++)
            {
                x=(i-1)*m+j-1;
                add(2*x,2*x+1,-map[i][j],1);//拆点构图,费用置为负
                add(2*x,2*x+1,0,1);
            }
        for(i=1; i<=n; i++)
            for(j=1; j<m; j++)//向右边建图
            {
                x=(i-1)*m+j-1;
                add(2*x+1,2*(x+1),0,2);
            }
        for(i=1; i<n; i++)
            for(j=1; j<=m; j++)//向下边建图
            {
                x=(i-1)*m+j-1;
                add(x*2+1,2*(x+m),0,2);
            }
        s=n*m*2;
        t=n*m*2+1;
        add(s,0,0,2);
        add(n*m*2-1,t,0,2);//连接源点汇点
        while(spfa())
            Add();
        cout<<-maxflow<<endl;
    }
}


这里再贴出poj 3422 题目连接:点击打开链接
题意上面说过了
代码:
#include<iostream>//和上面那题对比,这个是个n*n的 建图时好建,然后拆点,一条边流量为1,另一条为k-1
#include<cstring>
#include<queue>
using namespace std;
#define M 5050
#define N 100000
#define inf 0x7ffffff
struct node
{
    int u,v,c,f,next;
} a[N];
int head[M];
int visit[M];
int pre[M];
int dis[M];
int s,t,maxflow,num,k,n;
void add(int u,int v,int c,int f)
{
    a[num].u=u;
    a[num].v=v;
    a[num].f=f;
    a[num].c=c;
    a[num].next=head[u];
    head[u]=num++;
    a[num].u=v;
    a[num].v=u;
    a[num].f=0;
    a[num].c=-c;
    a[num].next=head[v];
    head[v]=num++;
}

int spfa()
{
    memset(pre,-1,sizeof(pre));
    memset(visit,0,sizeof(visit));
    for(int i=0; i<=t; i++)
        dis[i]=inf;
    dis[s]=0;
    queue<int>q;
    visit[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u]; i!=-1; i=a[i].next)
        {
            int v=a[i].v;
            if(a[i].f>0&&dis[u]+a[i].c<dis[v])
            {
                dis[v]=dis[u]+a[i].c;
                pre[v]=i;
                if(!visit[v])
                {
                    visit[v]=1;
                    q.push(v);
                }
            }
        }
        visit[u]=0;
    }
    if(dis[t]==inf)
        return 0;
    return 1;
}
void Add()
{
    int v;
    int mm=inf;
    for(v=pre[t]; a[v].u!=s; v=pre[a[v].u])
        mm=min(mm,a[v].f);
    for(v=pre[t]; a[v].u!=s; v=pre[a[v].u])
    {
        a[v].f-=mm;
        a[v^1].f+=mm;
        maxflow+=mm*a[v].c;
    }
}
int map[100][100];
int main()
{
    while(cin>>n>>k)
    {
        maxflow=0;
        s=n*n*2;
        t=s+1;
        num=0;
        memset(head,-1,sizeof(head));
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                cin>>map[i][j];
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
            {
                int b=(i-1)*n+j-1;
                add(b*2,2*b+1,-map[i][j],1);
                add(b*2,b*2+1,0,k-1);
            }
        for(int i=1; i<=n; i++)
            for(int j=1; j<n; j++)
            {
                int b=(i-1)*n+j-1;
                add(2*b+1,2*(b+1),0,k);
            }
        for(int i=1; i<n; i++)
            for(int j=1; j<=n; j++)
            {
                int b=(i-1)*n+j-1;
                add(b*2+1,2*(b+n),0,k);
            }
        add(s,0,0,k);
        add(n*n*2-1,t,0,k);
        while(spfa())
            Add();
        cout<<-maxflow<<endl;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值