加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

HDU 3657 巧妙最小割

(2010-09-30 19:42:14)
标签:

hdu

3657

巧妙最小割

it

分类: 网络流

题目描述:n*m的矩阵,每个位置都有一个正数,一开始你的分数是0,当你取走一个数字时,你的分数增加那个分数。如果你取完数字后,新出现了2个相邻的都是空的格子,那么你的分数减少2 * ( x & y),x,y是那两个格子的原始数值。

同时有一些附加条件,有一些格子的数字是必须拿走的。

解题报告:

首先,这种相邻格子的问题都会想到它是一个二分图:

横纵坐标为偶数的在左边,横纵坐标为奇数的在右边。

构图如下:

一个超级原点,和左边的点相连接,容量是其权值。

一个超级汇点,右边的点和其连接,容量是其权值。

如果左边的点x和右边的点y相邻,连接x,y容量为2 * (x & y)

这样的图的最小割的定义是:

一个完整的取数过程中的最小花费。

左边的点x和原点连接表示选x,不连接(切断,割)表示不选x,代价就是x的权值。同理右边的点也是。

如果左边的x和原点连接,右边的y和汇点连接,那么满足割的定义,xy之间如果有边,一定要切断,代价就是2 * (x & y)。

同时,为了满足一定要取一些点的附加条件,只要让那些点和对应的原点或者汇点容量为无穷大就可以了,这样割一定不会割那条无穷大。

综上,答案就是所有点的权值减去上述构图的最小割。

代码如下:

#include<iostream>
using namespace std;
#define Max 0x1fffffff
#define size 2502
struct edge{int from, to, val, next;}e[140000];
int v[size], que[size], dis[size], cnt, cur[size];
void insert(int from, int to, int va)
{
    e[cnt].from = from, e[cnt].to = to; e[cnt].val = va;
    e[cnt].next = v[from];v[from] = cnt++;
    e[cnt].from = to, e[cnt].to = from; e[cnt].val = 0;
    e[cnt].next = v[to];v[to] = cnt++;
}
bool bfs(int n, int s, int t)
{
    int head, tail, id;
    head = tail = 0; que[tail++] = s;
    memset(dis, -1, sizeof(int) * n);dis[s] = 0;
    while(head < tail) // bfs,得到顶点i的距s的最短距离dis[i]
        for(id = v[que[head++]]; id != -1; id = e[id].next)
            if (e[id].val > 0 && dis[e[id].to] == -1)
            {
                dis[e[id].to] = dis[e[id].from] + 1;
                que[tail++] = e[id].to;
                if (e[id].to == t) return true;
            }
    return false;
}

int Dinic(int n, int s, int t)
{
    int maxflow = 0, tmp, i;
    while(bfs(n, s, t))
    {
        int u = s, tail = 0;
        for(i = 0; i < n; i++) cur[i] = v[i];
        while(cur[s] != -1)
            if (u != t && cur[u] != -1 && e[cur[u]].val > 0 && dis[u] != -1 && dis[u] + 1 == dis[e[cur[u]].to])
            {que[tail++] = cur[u]; u = e[cur[u]].to;}
            else if (u == t)
            {
                for(tmp = Max, i = tail - 1; i >= 0; i--) tmp = min(tmp, e[que[i]].val);
                for(maxflow += tmp, i = tail - 1; i >= 0; i--)
                {
                    e[que[i]].val -= tmp;
                    e[que[i] ^ 1].val += tmp;
                    if (e[que[i]].val == 0) tail = i;
                }
                u = e[que[tail]].from;
            }
            else
            {
                while(tail > 0 && u != s && cur[u] == -1) u = e[que[--tail]].from;
                cur[u] = e[cur[u]].next;
            }
    }
    return maxflow;
}
int n, m, kk, g[50][50], move[4][2] = {0, 1, 0, -1, 1, 0, -1, 0}, tmp[50][50];
int main()
{
    while(scanf("%d%d%d", &n, &m, &kk) != EOF)
    {
        memset(v, -1, sizeof(int) * (n * m + 2)); cnt = 0;
        int s = n * m, t = n * m + 1, sum = 0;
        for(int i = 0; i < n; i++) for(int j = 0; j < m; j++)
        {
            scanf("%d", &g[i][j]);
            tmp[i][j] = cnt;
            if ((i + j) % 2) insert(i * m + j, t, g[i][j]);
            else insert(s, i * m + j, g[i][j]);
            sum += g[i][j];
        }
        for(int i = 0; i < kk; i++)
        {
            int a, b;scanf("%d%d", &a, &b);a--, b--;
            e[tmp[a][b]].val = Max;

        }
        for(int i = 0; i < n; i++) for(int j = 0; j < m; j++)
            if ((i + j) % 2 == 0)
            for(int k = 0; k < 4; k++)
            {
                int a = i + move[k][0], b = j + move[k][1];
                if (a >= 0 && a < n && b >= 0 && b < m) insert(i * m + j, a * m + b, 2 * (g[i][j] & g[a][b]));
            }
        printf("%d\n", sum - Dinic(t + 1, s, t));
    }
    return 0;
}

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有