zoukankan      html  css  js  c++  java
  • 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 ;
    }
    


  • 相关阅读:
    面试点滴
    算法之归并排序
    博客园代码高亮样式更换-测试
    MacOS 10.12 设置找不到 任何来源 的话 这么操作 教程
    HTTP代理协议 HTTP/1.1的CONNECT方法
    Linux命令
    Linux命令
    Linux命令
    vmware虚拟机linux桥接模式设置
    GDB调试 (七)
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3061602.html
Copyright © 2011-2022 走看看