您的位置:首页 > 其它

hdoj 3657 Game 【最小割 方格填数加强版】

2015-08-31 20:09 232 查看

Game

Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 1076 Accepted Submission(s): 456



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


终于明白了!建议先做 方格取数(2)

题意:有一个N*M的方格且每个格子都有一定的数值。初始你的分数为0,你可以任意选择并拿走一个方格中的数值,相应的你的分数也会加上该数值。若你选择拿走两个相邻方格的数值,那分数将会减去2 * (x & y),其中x、y为两个相邻格子的数值。 又给你一个要求——你必须要拿走k个格子的数值。问你能得到的最大分数。

思路:构二分图,然后建立最小割模型求解。

做本题要理解的地方!

对于路径source -> x -> y -> sink,我们根据最小割的边来讨论所表示的意义。

一、若割去source -> x,说明不选x点选y点;

二、若割去y->sink,说明不选y点选x点;

三、若割去x->y,说明既选x点又选y点。

至于为什么构造二分图,我就不说明了,只说下建图。

建图:设置超级源点source,超级汇点sink,横纵坐标和为奇数的点集为S集,横纵坐标和为偶数的点集为T集。

1,source向S集里面的点建边,容量为点权,表示不选该点付出的代价;

2,T集里面的点向sink建边,容量为点权,表示不选该点付出的代价;

3,S集里面的点向它临近的T集里面的点建边,容量为2 * (x & y),x、y为两点的点权,表示选择两点需要付出的代价。

4,对于必须要选的点。若它在S集,则source向它建边,容量为无穷大,表示不选这个点的付出的代价为无穷,所以必须要选。同理,若它T集,则向sink建边,容量为无穷大。

跑一次最大流——最小割,再用数值总和减去就行了。

AC代码:

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define MAXN 3000
#define MAXM 50000
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
    int from, to, cap, flow, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int dist[MAXN], cur[MAXN];
bool vis[MAXN];
int N, M, k;
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
int point(int x, int y)
{
    return (x-1) * M + y;
}
void addEdge(int u, int v, int w)
{
    Edge E1 = {u, v, w, 0, head[u]};
    edge[edgenum] = E1;
    head[u] = edgenum++;
    Edge E2 = {v, u, 0, 0, head[v]};
    edge[edgenum] = E2;
    head[v] = edgenum++;
}
int sum;//记录数值总和
int Map[60][60];
bool judge(int x, int y)
{
    return x >= 1 && x <= N && y >= 1 && y <= M && (x + y) % 2 == 0;
}
int source, sink;//超级源点 超级汇点
void getMap()
{
    int a, b;
    source = 0, sink = N*M+1;
    sum = 0;
    for(int i = 1; i <= N; i++)
    {
        for(int j = 1; j <= M; j++)
        {
            scanf("%d", &Map[i][j]);
            sum += Map[i][j];
            if((i + j) & 1)//奇数
                addEdge(source, point(i, j), Map[i][j]);
            else//偶数
                addEdge(point(i, j), sink, Map[i][j]);
        }
    }
    while(k--)
    {
        scanf("%d%d", &a, &b);
        if((a + b) & 1)
            addEdge(source, point(a, b), INF);//一定要选 该边不能被割掉
        else
            addEdge(point(a, b), sink, INF);
    }
    int move[4][2] = {0,1, 0,-1, 1,0, -1,0};
    for(int i = 1; i <= N; i++)
    {
        for(int j = 1; j <= M; j++)
        {
            if((i + j) & 1)
            {
                for(int k = 0; k < 4; k++)
                {
                    int x = i + move[k][0];
                    int y = j + move[k][1];
                    if(judge(x, y))//S集的点 向T集的点 建边
                        addEdge(point(i, j), point(x, y), 2*(Map[i][j] & Map[x][y]));
                }
            }
        }
    }
}
bool BFS(int s, int t)
{
    queue<int> Q;
    memset(dist, -1, sizeof(dist));
    memset(vis, false, sizeof(vis));
    dist[s] = 0;
    vis[s] = true;
    Q.push(s);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(!vis[E.to] && E.cap > E.flow)
            {
                dist[E.to] = dist[u] + 1;
                if(E.to == t) return true;
                vis[E.to] = true;
                Q.push(E.to);
            }
        }
    }
    return false;
}
int DFS(int x, int a, int t)
{
    if(x == t || a == 0) return a;
    int flow = 0, f;
    for(int &i = cur[x]; i != -1; i = edge[i].next)
    {
        Edge &E = edge[i];
        if(dist[E.to] == dist[x] + 1 && (f = DFS(E.to, min(a, E.cap-E.flow), t)) > 0)
        {
            edge[i].flow += f;
            edge[i^1].flow -= f;
            flow += f;
            a -= f;
            if(a == 0) break;
        }
    }
    return flow;
}
int Maxflow(int s, int t)
{
    int flow = 0;
    while(BFS(s, t))
    {
        memcpy(cur, head, sizeof(head));
        flow += DFS(s, INF, t);
    }
    return flow;
}
int main()
{
    while(scanf("%d%d%d", &N, &M, &k) != EOF)
    {
        init();
        getMap();
        printf("%d\n", sum - Maxflow(source, sink));
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: