HDU 3376 费用流

题意:给你一个矩阵,问从左上走到右下再从右下回到左上,每个点只能走一次,问最大值是多少。

思路:一看就知道是费用流,但是在建图这里卡住了。因为一开始不知道怎么从右下重新回到左上。

写了很久建图的过程,最后挂了 。然后参考了别人的图,发现只要在插入一个源点与起点连接,容量是2,费用是0,终点与汇点相连,容量是2,费用0。这样就能控制来回两次的问题。

关于建图,将每个点拆成i , i + n * n .容量是1 ,费用是该点的值,容量1就能控制每个点只走一次。当然起点和终点的容量得是2。

假设i和j可以相连,那么将i + n * n 与j相连,容量是inf ,费用是0 ,当然这题我容量直接是2了,因为最大流就是2。

源点与起点相连,容量为2,费用为0.

终点与汇点相连,容量为2,费用为0.

 

#include <iostream>

#include <cstdio>

#include <algorithm>

#include <string>

#include <cmath>

#include <cstring>

#include <queue>

#include <set>

#include <vector>

#include <stack>

#include <map>

#include <iomanip>

#define PI acos(-1.0)

#define Max 2005

#define inf 1<<28

#define LL(x) (x<<1)

#define RR(x) (x<<1|1)

#define REP(i,s,t) for(int i=(s);i<=(t);++i)

#define ll long long

#define mem(a,b) memset(a,b,sizeof(a))

#define mp(a,b) make_pair(a,b)

using namespace std;

inline void readint(int &ret)

{

    char c;

    do

    {

        c = getchar();

    }

    while(c < '0' || c > '9');

    ret = c - '0';

    while((c=getchar()) >= '0' && c <= '9')

        ret = ret * 10 + ( c - '0' );

}



int Map[666][666] ;

int inmap(int x ,int y ,int n )

{

    if(x >0 && x <= n && y > 0 && y <= n) return 1 ;

    return 0 ;

}

int mx[2] = {0,1} ;

int my[2] = {1,0} ;



struct kdq

{

    int s , e, l , c ,next ;

} ed[21111111] ;

int head[1111111] ,num ;

void add(int s ,int e ,int l ,int c )

{

    ed[num].s = s ;

    ed[num].e = e ;

    ed[num].l = l ;

    ed[num].c = c ;

    ed[num].next = head[s] ;

    head[s] = num ++ ;



    ed[num].s = e ;

    ed[num].e = s ;

    ed[num].l = 0 ;

    ed[num].c = -c ;

    ed[num].next = head[e] ;

    head[e] = num ++ ;

}

int S , T ;

int dis[1111111] ;

bool vis[1111111] ;

int qe[11111111] ;

int pre[1111111] ;

int path[1111111] ;

int spfa()

{

//    REP(i,1,T) dis[i] = inf ;

    mem(dis,-1) ;

    dis[S] = 0 ;

    vis[S] = 1 ;

    int h = 0 ,t = 0 ;

    qe[h ++ ] = S ;

    while( h > t )

    {

        int tt  = qe[t ++ ] ;

        //cout << tt <<endl;

        vis[tt] = 0 ;

        for (int i  = head[tt]  ; ~i ; i = ed[i].next  )

        {

            int e = ed[i].e ;

            int l = ed[i].l ;

            int c = ed[i].c ;

//            cout << tt <<" " << e << " " << c << endl;

            //cout << e << endl;

            if(l > 0 &&dis[e] < dis[tt] + c)

            {

                pre[e] = tt ;

                path[e] = i ;

                dis[e] = dis[tt] + c ;

                if(!vis[e])

                {

                    vis[e] = 1 ;

                    qe[h ++ ] = e ;

                }

            }

        }

    }

    return dis[T] != -1 ;

}



int MFMC()

{

    int mic = 0 ;

    while(spfa())

    {

        int flow = inf ;

        for (int i = T ; i != S ; i = pre[i])

        {

            flow = min(flow ,ed[path[i]].l) ;

        }

        for (int i = T ; i != S ; i = pre[i])

        {

            ed[path[i]].l -= flow ;

            ed[path[i] ^ 1].l += flow ;

        }

        mic += dis[T] * flow ;

    }

    return mic ;

}





void init()

{

    mem(head,-1) ;

    num = 0 ;

}

int main()

{

    int n ;

    while(cin >> n )

    {

        init() ;

        S = 0 ;

        T = n * n + n * n + 1 ;

        REP(i,1,n)REP(j,1,n)readint(Map[i][j]) ;

        REP(i,1,n)

        {

            REP(j,1,n)

            {

                add((i - 1 ) * n + j , n * n + (i - 1 )  * n + j , 1, Map[i][j]) ;//拆点,i -> i + n * n ,容量1,费用为该点值 

                REP(k,0,1)

                {

                    int tx = i + mx[k] ;

                    int ty = j + my[k] ;



                    if(inmap(tx,ty,n))

                    {

                        add(n * n + (i - 1 ) * n + j , ( tx - 1 ) * n + ty ,2 ,0) ;//若两点可达, i + n * n -> j ,容量为inf ,费用为0 .

                    }

                }

            }

        }

        add(1,1 + n * n , 1, Map[1][1]) ;//起点容量+1

        add(n * n , n * n * 2 ,1,Map[n][n]) ;//终点容量+1

        add(S,1,2,0) ;//源点到起点,容量2,费用0

        add(n * n + n * n , T ,2,0) ;//终点到汇点,容量2,费用0 

        printf("%d\n",MFMC() - Map[1][1] - Map[n][n]) ;//因为起点和终点走了2次,所以要减去他的值,当然也可以在重新加边的时候费用为0即可。



    }

    return 0 ;

}


 

 

你可能感兴趣的:(HDU)