Matrix

 

Description

Yifenfei 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 yifenfei should to do is that choose a detour which frome 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 yifenfei 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 yifenfei can not pass the same area of the Matrix except the start and end.        
              

Input

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

Output

For each test case output the maximal values yifenfei 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

 

依然详见注释。。还有这个题是来回的求最大费用最大流

#include <iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#define oo 1<<28
#include<queue>
#include<algorithm>
using namespace std;
int pre[210000];
int dis[210000];
int vist[210000];
int head[210000];
int map[10000][10000];
struct node
{
    int u;
    int v;//与u点相连的点
    int f;//容量
    int w;//权值
    int next;//下一条边
} edge[1100000];
int cnt=0;
int s,t;
void add(int u,int v,int f,int w)
{
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].f=f;
    edge[cnt].w=w;
    edge[cnt].next=head[u];
    head[u]=cnt++;

    edge[cnt].u=v;
    edge[cnt].v=u;
    edge[cnt].f=0;
    edge[cnt].w=-w;//反向的权值为正向的相反数
    edge[cnt].next=head[v];
    head[v]=cnt++;
}
void init()
{
    memset(pre,-1,sizeof(pre));
    memset(vist,0,sizeof(vist));
    memset(dis,-1,sizeof(dis));//要求最大值
}
int spfa()
{
    int i;
    init();
    queue<int>q;
    dis[s]=0;
    vist[s]=1;
    q.push(s);
    while(!q.empty())//将相邻点进行松弛,直到队列为空
    {
        int u=q.front();//取出队头
        q.pop();
        i=head[u];
        vist[u]=0;
        while(i!=-1)
        {
            int w=edge[i].w;
            int v=edge[i].v;
            if(edge[i].f>0&&dis[v]<dis[u]+w)//判断是否可以更新
            {
                dis[v]=dis[u]+w;//改进s到v点的值
                pre[v]=i;//记录此点的前驱
                if(!vist[v])//由于距离变小了,如果v点松弛成功且v点不在队列里,因为v点有可能还能改进别的点
                {
                    vist[v]=1;
                    q.push(v);
                }
            }
            i=edge[i].next;
        }
    }
    if(pre[t]==-1)//如果不在最短路中代表寻找失败
        return 0;
    return 1;
}
int Cost()
{
    int ans=0;
    while(spfa())//如果增广路寻找成功
    {
        int maxx=oo;
        int p=pre[t];//初始化P指针
        while(p!=-1)
        {
            maxx=min(edge[p].f,maxx);//求出此增广路上边的最小值
            p=pre[edge[p].u];
        }
        p=pre[t];
        while(p!=-1)
        {
            edge[p].f-=maxx;//正向减去
            edge[p^1].f+=maxx;//反向增加
            ans+=maxx*edge[p].w;//因为以单位计费,所以应当乘上流量
            p=pre[edge[p].u];
        }
    }
    return ans;
}
int main()
{
    //我觉得我最近喜欢有的题看题解,感觉特别不是很好,虽然大部分都理解了,也能敲出来,但是少了自己思考的一部分
    //这个题依然要拆点,就是将一个点拆成u,v,自身到自身建边,还有自身到右和下建边
    //先由源点到1点,n*n*2到汇点建一条容量为2,权值为0的边,因为是来回,在由自身((i-1)*n+j)*2-1到((i-1)*n+j)*2建两条边
    //一条为容量为1,权值为map[i][j],一条为容量为1,权值为0的边,因为每个点只能走一遍,最后将自身的v点和右和下的u相连,
    //建一条容量为2,权值为0的边,依然注意i,j在边的时候
    //这个和C题有点相似,但是因为这个拆点,且点不能走两边,所以自身建边容量为1
    int n,m,d,k;
    int i,j,l;
    while(~scanf("%d",&n))
    {
         memset(head,-1,sizeof(head));
         int u,v,w;
         for(i=1;i<=n;i++)
         {
             for(j=1;j<=n;j++)
             {
                 scanf("%d",&map[i][j]);
             }
         }
         s=0,t=n*n*2+1;
         add(s,1,2,0);//源点与1点相连
         add(n*n*2,t,2,0);//n*n*2与汇点相连
         for(i=1;i<=n;i++)
         {
             for(j=1;j<=n;j++)
             {
                 int a=((i-1)*n+j)*2-1;
                 int b=((i-1)*n+j)*2;
                 //printf("%d %d\n",a,b);
                 add(a,b,1,map[i][j]);//自身出点,入点相连
                 add(a,b,1,0);
                 if(i!=n&&j!=n)
                 {
                     add(b,b+1,2,0);//向右
                     add(b,b+2*n-1,2,0);//向下
                     //printf("%d %d %d\n",b,b+1,b+2*n-1);
                 }
                 else if(i!=n&&j==n)
                 {
                     add(b,b+2*n-1,2,0);
                 }
                 else if(i==n&&j!=n)
                 {
                     add(b,b+1,2,0);
                 }
             }
         }
         printf("%d\n",Cost());
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值