zoukankan      html  css  js  c++  java
  • HDU 3820 Golden Eggs( 最小割 奇特建图)经典

    Golden Eggs

    Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 501    Accepted Submission(s): 281


    Problem Description
    There is a grid with N rows and M columns. In each cell you can choose to put a golden or silver egg in it, or just leave it empty. If you put an egg in the cell, you will get some points which depends on the color of the egg. But for every pair of adjacent eggs with the same color, you lose G points if there are golden and lose S points otherwise. Two eggs are adjacent if and only if there are in the two cells which share an edge. Try to make your points as high as possible.
     

    Input
    The first line contains an integer T indicating the number of test cases.
    There are four integers N, M, G and S in the first line of each test case. Then 2*N lines follows, each line contains M integers. The j-th integer of the i-th line Aij indicates the points you will get if there is a golden egg in the cell(i,j). The j-th integer of the (i+N)-th line Bij indicates the points you will get if there is a silver egg in the cell(i,j).

    Technical Specification
    1. 1 <= T <= 20
    2. 1 <= N,M <= 50
    3. 1 <= G,S <= 10000
    4. 1 <= Aij,Bij <= 10000
     

    Output
    For each test case, output the case number first and then output the highest points in a line.
     

    Sample Input
    2 2 2 100 100 1 1 5 1 1 4 1 1 1 4 85 95 100 100 10 10 10 10 100 100
     

    Sample Output
    Case 1: 9 Case 2: 225
     

    Author
    hanshuai
     

    Source
    题意:有一个n*m的格子。能够放两种颜色我蛋:金蛋和银蛋。假设相邻格子里放同一颜色的蛋:假设为金蛋则降低价值为G,假设银蛋则降低价值为S。接下来n行m列表示放金蛋得到的价值mp1[][],再接下来n行m列表示放银蛋得到的价值mp2[][]。问最大能得到的价值。
    解题:vs:源点,vt:汇点。建图:把n*m个格子分成奇偶两部分。每一个点都须要拆成两个点v,v'。奇点v:<vs , v , mp1[i][j]>。<v , v' , INF>。<v' , vt , mp2[i][j]>。与v相邻点u。<v , u' , G>。

    偶点u:<vs , u , mp2[i][j]>。<u , u' , INF>,<u' , vt , mp1[i][j]>。

    与u相邻点v。<u , v' , S>。这样图建好了,那么答案:mp1[][]+mp2[][] -maxflow。

    /*
    最大流:SAP算法,与ISAP的区别就是不用预处理
    */
    #include<stdio.h>
    #include<string.h>
    #include<queue>
    #include<algorithm>
    using namespace std;
    #define captype int
    
    const int MAXN = 100010;   //点的总数
    const int MAXM = 400010;    //边的总数
    const int INF = 1<<30;
    struct EDG{
        int to,next;
        captype cap,flow;
    } edg[MAXM];
    int eid,head[MAXN];
    int gap[MAXN];  //每种距离(或可觉得是高度)点的个数
    int dis[MAXN];  //每一个点到终点eNode 的最短距离
    int cur[MAXN];  //cur[u] 表示从u点出发可流经 cur[u] 号边
    int pre[MAXN];
    
    void init(){
        eid=0;
        memset(head,-1,sizeof(head));
    }
    //有向边 三个參数。无向边4个參数
    void addEdg(int u,int v,captype c,captype rc=0){
        edg[eid].to=v; edg[eid].next=head[u];
        edg[eid].cap=c; edg[eid].flow=0; head[u]=eid++;
    
        edg[eid].to=u; edg[eid].next=head[v];
        edg[eid].cap=rc; edg[eid].flow=0; head[v]=eid++;
    }
    captype maxFlow_sap(int sNode,int eNode, int n){//n是包含源点和汇点的总点个数。这个一定要注意
        memset(gap,0,sizeof(gap));
        memset(dis,0,sizeof(dis));
        memcpy(cur,head,sizeof(head));
        pre[sNode] = -1;
        gap[0]=n;
        captype ans=0;  //最大流
        int u=sNode;
        while(dis[sNode]<n){   //推断从sNode点有没有流向下一个相邻的点
            if(u==eNode){   //找到一条可增流的路
                captype Min=INF ;
                int inser;
                for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to])    //从这条可增流的路找到最多可增的流量Min
                if(Min>edg[i].cap-edg[i].flow){
                    Min=edg[i].cap-edg[i].flow;
                    inser=i;
                }
                for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to]){
                    edg[i].flow+=Min;
                    edg[i^1].flow-=Min;  //可回流的边的流量
                }
                ans+=Min;
                u=edg[inser^1].to;
                continue;
            }
            bool flag = false;  //推断是否能从u点出发可往相邻点流
            int v;
            for(int i=cur[u]; i!=-1; i=edg[i].next){
                v=edg[i].to;
                if(edg[i].cap-edg[i].flow>0 && dis[u]==dis[v]+1){
                    flag=true;
                    cur[u]=pre[v]=i;
                    break;
                }
            }
            if(flag){
                u=v;
                continue;
            }
            //假设上面没有找到一个可流的相邻点。则改变出发点u的距离(也可觉得是高度)为相邻可流点的最小距离+1
            int Mind= n;
            for(int i=head[u]; i!=-1; i=edg[i].next)
            if(edg[i].cap-edg[i].flow>0 && Mind>dis[edg[i].to]){
                Mind=dis[edg[i].to];
                cur[u]=i;
            }
            gap[dis[u]]--;
            if(gap[dis[u]]==0) return ans;  //当dis[u]这样的距离的点没有了。也就不可能从源点出发找到一条增广流路径
                                            //由于汇点到当前点的距离仅仅有一种,那么从源点到汇点必定经过当前点。然而当前点又没能找到可流向的点,那么必定断流
            dis[u]=Mind+1;//假设找到一个可流的相邻点,则距离为相邻点距离+1,假设找不到。则为n+1
            gap[dis[u]]++;
            if(u!=sNode) u=edg[pre[u]^1].to;  //退一条边
        }
        return ans;
    }
    int main()
    {
        int T,n,m,vs,vt,mp1[55][55],mp2[55][55],G,S;
        int dir[4][2]={0,1,0,-1,1,0,-1,0};
        scanf("%d",&T);
        for(int _case=1; _case<=T; ++_case){
    
            int ans=0;
    
            scanf("%d%d%d%d",&n,&m,&G,&S);
            for(int i=0; i<n; i++)
                for(int j=0; j<m; j++)
                  scanf("%d",&mp1[i][j]) , ans+=mp1[i][j];
            for(int i=0; i<n; i++)
                for(int j=0; j<m; j++)
                  scanf("%d",&mp2[i][j]) , ans+=mp2[i][j];
    
            vs = 2*n*m; vt = vs+1;
            init();
            for(int i=0; i<n; i++)
                for(int j=0; j<m; j++){
                    int u=i*m+j;
                      if((i+j)&1){
                         addEdg(vs , u , mp1[i][j]);
                         addEdg(u , u+n*m , INF);
                         addEdg(u+n*m , vt , mp2[i][j]);
                         for(int e=0; e<4; e++)
                         {
                             int ti , tj;
                             ti=i+dir[e][0];
                             tj=j+dir[e][1];
                             if(ti>=0&&ti<n&&tj>=0&&tj<m)
                                addEdg(u , ti*m+tj+n*m , G);
                         }
                      }
                      else{
                        addEdg(vs , u , mp2[i][j]);
                         addEdg(u , u+n*m , INF);
                         addEdg(u+n*m , vt , mp1[i][j]);
                         for(int e=0; e<4; e++)
                         {
                             int ti , tj;
                             ti=i+dir[e][0];
                             tj=j+dir[e][1];
                             if(ti>=0&&ti<n&&tj>=0&&tj<m)
                                addEdg(u , ti*m+tj+n*m , S);
                         }
                      }
                }
            ans-=maxFlow_sap(vs , vt , vt+1);
            printf("Case %d: %d
    ",_case , ans);
        }
    }
    


  • 相关阅读:
    [LeetCode] String to Integer (atoi) 解题报告
    [LeetCode] Spiral Matrix 解题报告
    推导基姆拉尔森公式根据日期计算星期
    gdb常用命令的用法
    利用基姆拉尔森公式根据日期计算星期
    RIM推出BlackBerry SDK 助力开发者多种应用程序开发
    ERP环境下物料清单的数据结构研究[转]
    VSTO EXCEL篇学习笔记五【原】
    高德纳传奇[转]
    PLM中BOM核心技术的研究[转]
  • 原文地址:https://www.cnblogs.com/mthoutai/p/7205733.html
Copyright © 2011-2022 走看看