hdoj 3376 Matrix Again and hdoj 2686 Matrix 【最大费用最大流】



Matrix Again

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 102400/102400 K (Java/Others)
Total Submission(s): 3453    Accepted Submission(s): 1017


Problem Description
Starvae very like play a number game in the n*n Matrix. A positive integer number is put in each area of the Matrix.
Every time starvae should to do is that choose a detour which from the top left point to the bottom right point and than back to the top left point with the maximal values of sum integers that area of Matrix starvae choose. But from the top to the bottom can only choose right and down, from the bottom to the top can only choose left and up. And starvae can not pass the same area of the Matrix except the start and end..
Do you know why call this problem as “Matrix Again”? AS it is like the problem 2686 of HDU.
 

Input
The input contains multiple test cases.
Each case first line given the integer n (2<=n<=600) 
Then n lines, each line include n positive integers. (<100)
 

Output
For each test case output the maximal values starvae can get.
 

Sample Input
      
      
2 10 3 5 10 3 10 3 3 2 5 3 6 7 10 5 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9
 

Sample Output
      
      
28 46 80
 



无语死了,TLE到死。还好最后用G++卡过了,C++死活过不了。


两道题意一样,就是数据卡的不一样!!!

题意:给你一个N*N的矩阵,每个元素代表该处的权值。要求每个点只能走一次,左上角和右下角可以走两次但该处的权值只能获取一次。问你从左上角走到右下角(只能向下或右移动),再从右下角回到左上角(只能向上或左移动)所能得到的最大权值。


思路:把所有元素虚拟成N*N个点,把可达关系当做一条边,可走次数作为边的容量,权值当做边的费用。问题就变成了最大费用最大流。


建图:设置超级源点source,超级汇点sink

1,把所有点i拆分左点i 和 右点i + N*N,i到i+N*N建边,容量为1(只有当i为起点或者终点时容量才为2),费用为点权。

2,source连接左上角的左点,容量为2,费用0;

3,右下角右点连接sink,容量为2,费用0;

4,所有可达关系,即对于坐标(x, y) 向(x+1, y)和(x, y+1)建边(边界需要讨论),容量为1(至少1可以大于1),费用0。

最后跑一遍最大费用最大流后,减去起点权值和终点权值(我们多算了一次)就ok了。理解最小费用后求解最大费用很简单的,只需要把SPFA过程改为查找S-T的最大费用路径。


AC代码: 可以过这两个题


#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <stack>
#include <algorithm>
#define MAXN 800000+10
#define MAXM 4000000+10
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
    int from, to, cap, flow, cost, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int pre[MAXN], dist[MAXN];
bool vis[MAXN];
int N;
int Map[610][610];
int source, sink;
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w, int c)
{
    edge[edgenum].from = u;
    edge[edgenum].to = v;
    edge[edgenum].cap = w;
    edge[edgenum].flow = 0;
    edge[edgenum].cost = c;
    edge[edgenum].next = head[u];
    head[u] = edgenum++;
    edge[edgenum].from = v;
    edge[edgenum].to = u;
    edge[edgenum].cap = 0;
    edge[edgenum].flow = 0;
    edge[edgenum].cost = -c;
    edge[edgenum].next = head[v];
    head[v] = edgenum++;
}
int point(int x, int y)
{
    return (x-1)*N + y;
}
void getMap()
{
    int k = N*N;
    source = 0; sink = 2*k+1;
    for(int i = 1; i <= N; i++)
    {
        for(int j = 1; j <= N; j++)
        {
            scanf("%d", &Map[i][j]);
            if(i == 1 && j == 1 || i == N && j == N)
                addEdge(point(i, j), point(i, j) + k, 2, Map[i][j]);//起点和终点 容量为2 费用为点权
            else
                addEdge(point(i, j), point(i, j) + k, 1, Map[i][j]);//左点连右点 容量为1 费用为点权
            if(i < N)
                addEdge(point(i, j)+k, point(i+1, j), 1, 0);//右点 连 左点 容量为1 费用0
            if(j < N)
                addEdge(point(i, j)+k, point(i, j+1), 1, 0);
        }
    }
    addEdge(source, 1, 2, 0);//超级源点 连起点的左点 容量2 费用为0
    addEdge(point(N, N)+k, sink, 2, 0);//终点的右点 连超级汇点 容量2 费用为0
}
bool SPFA(int s, int t)
{
    queue<int> Q;
    memset(dist, -INF, sizeof(dist));
    memset(vis, false, sizeof(vis));
    memset(pre, -1, sizeof(pre));
    dist[s] = 0;
    vis[s] = true;
    Q.push(s);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        vis[u] = false;
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(dist[E.to] < dist[u] + E.cost && E.cap > E.flow)//最大费用 且 无满流路径
            {
                dist[E.to] = dist[u] + E.cost;
                pre[E.to] = i;
                if(!vis[E.to])
                {
                    vis[E.to] = true;
                    Q.push(E.to);
                }
            }
        }
    }
    return pre[t] != -1;
}
void MCMF(int s, int t, int &cost, int &flow)
{
    cost = flow = 0;
    while(SPFA(s, t))
    {
        int Min = INF;
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
        {
            Edge E = edge[i];
            Min = min(Min, E.cap-E.flow);
        }
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
        {
            edge[i].flow += Min;
            edge[i^1].flow -= Min;
            cost += edge[i].cost * Min;
        }
        flow += Min;
    }
}
int main()
{
    while(scanf("%d", &N) != EOF)
    {
        init();
        getMap();
        int cost, flow;
        MCMF(source, sink, cost, flow);
        cost -= Map[1][1] + Map[N][N];//多算了起点和终点的值
        printf("%d\n", cost);
    }
    return 0;
}



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值