1. 程式人生 > >HDU 3657 Game(取數 最小割)經典

HDU 3657 Game(取數 最小割)經典

next 是否 sco gree mission problem posit -1 appear

Game

Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1065 Accepted Submission(s): 449


Problem Description onmylove has invented a game on n × m grids. There is one positive integer on each grid. Now you can take the numbers from the grids to make your final score as high as possible. The way to get score is like
the following:
● At the beginning, the score is 0;
● If you take a number which equals to x, the score increase x;
● If there appears two neighboring empty grids after you taken the number, then the score should be decreased by 2(x&y). Here x and y are the values used to existed on these two grids. Please pay attention that "neighboring grids" means there exits and only exits one common border between these two grids.

Since onmylove thinks this problem is too easy, he adds one more rule:
● Before you start the game, you are given some positions and the numbers on these positions must be taken away.
Can you help onmylove to calculate: what‘s the highest score onmylove can get in the game?
Input Multiple input cases. For each case, there are three integers n, m, k in a line.
n and m describing the size of the grids is n ×m. k means there are k positions of which you must take their numbers. Then following n lines, each contains m numbers, representing the numbers on the n×m grids.Then k lines follow. Each line contains two integers, representing the row and column of one position
and you must take the number on this position. Also, the rows and columns are counted start from 1.
Limits: 1 ≤ n, m ≤ 50, 0 ≤ k ≤ n × m, the integer in every gird is not more than 1000.
Output For each test case, output the highest score on one line.

Sample Input
2 2 1
2 2
2 2
1 1
2 2 1
2 7
4 1
1 1

Sample Output
4
9

HintAs to the second case in Sample Input, onmylove gan get the highest score when calulating like this:
2 + 7 + 4 - 2 × (2&4) - 2 × (2&7) = 13 - 2 × 0 - 2 × 2 = 9. 

Author onmylove
Source 2010 Asia Regional Chengdu Site —— Online Contest

題目描寫敘述:n*m的矩陣,每一個位置都有一個正數,一開始你的分數是0。當你取走一個數字時,你的分數添加那個分數。假設你取完數字後。新出現了2個相鄰的都是空的格子,那麽你的分數降低2 * ( x & y),x,y是那兩個格子的原始數值。

同一時候有一些附加條件,有一些格子的數字是必須拿走的。

解題:與方格取數幾乎相同。註要多了兩個不同的條件。

1.取相鄰的格子則要降低2*(x&y) 建圖時。相鄰兩個格子之間建邊容量為2*(x&y) 2.有K個格子是必須取的,則與必須取的點相連的點S或T的邊的容量為INF。這樣在求最小割時就不會被割了。

#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 n,m,k,cost[55][55],flag[55][55];
    int dir[4][2]={0,1,0,-1,1,0,-1,0};
    while(scanf("%d%d%d",&n,&m,&k)>0)
    {
        init();
        int s=n*m,t=n*m+1 , ans=0;
        for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
        {
            scanf("%d",&cost[i][j]);
           ans+=cost[i][j];
        }
        int x,y;
        memset(flag,0,sizeof(flag));
        while(k--)
        {
            scanf("%d%d",&x,&y); x--; y--;
            flag[x][y]=1;
        }
        for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
        if((i+j)&1)
        {
            addEdg(s , i*m+j , flag[i][j]==0?cost[i][j]:INF);
            for(int e=0; e<4; e++)
             {
                 x=i+dir[e][0];
                 y=j+dir[e][1];
                 if(x>=0&&x<n&&y>=0&&y<m)
                  addEdg(i*m+j, x*m+y,2*(cost[i][j]&cost[x][y]));
             }
        }
        else
            addEdg(i*m+j,t,flag[i][j]==0?cost[i][j]:INF);
            
        ans-=maxFlow_sap(s , t, t+1);
        printf("%d\n",ans);
    }
}


HDU 3657 Game(取數 最小割)經典