zoukankan      html  css  js  c++  java
  • HDU 1569 方格取数(2)

    HDU_1569

         一开始没有什么思路,后来看了别人的解题报告说要去构造一个二分图转化成最小割去做。

    后来我又琢磨了一下,大概原理是这样的,如果把原图按i+j的奇偶性分成两部分(也就是染成国际象棋棋盘那样),并且相邻的格子之间连一条边的话,就变成了求最大权独立集了,而最大权独立集又等于SUM减去最小点权覆盖集,这样我们就转化成去求最小点权覆盖集了。而如果我们构造一个超级源点和二分图左边所有点相连并且边权为格子的权值,同时另二分图右边所有点和构造的一个超级汇点相连并且边权为格子的权值,并且把二分图中间的边的边权设为INF,这样求出的最小割就是最小点权覆盖集。

    因为如果是最小点权覆盖集的话,拿掉这些点及其覆盖的边,原图一定不通(如果与源点或者汇点相连的边都被覆盖,那么二分图所有边一定会被覆盖,因此只要能够覆盖二分图的边即可),同时又因为是最小点权覆盖,所以最小点权覆盖集是原图的最小割。

    #include<stdio.h>
    #include<string.h>
    #define MAXD 3000
    #define MAXM 36000
    #define INF 100000000
    int N, M, SUM;
    int first[MAXD], next[MAXM], u[MAXM], v[MAXM], flow[MAXM], e;
    int s[MAXD], d[MAXD], q[MAXD], work[MAXD];
    int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
    void add(int a, int b, int w)
    {
    u[e] = a;
    v[e] = b;
    flow[e] = w;
    next[e] = first[a];
    first[a] = e;
    e ++;
    }
    int init()
    {
    int i, j, k, a;
    if(scanf("%d%d", &M, &N) != 2)
    return 0;
    memset(first, -1, sizeof(first));
    e = SUM = 0;
    for(i = 1; i <= M; i ++)
    for(j = 1; j <= N; j ++)
    {
    scanf("%d", &a);
    SUM += a;
    if((i + j) % 2 == 0)
    {
    add(0, i * N + j, a);
    add(i * N + j, 0, 0);
    for(k = 0; k < 4; k ++)
    {
    int x = i + dx[k];
    int y = j + dy[k];
    if(x >= 1 && x <= M && y >= 1 && y <= N)
    {
    add(i * N + j, x * N + y, INF);
    add(x * N + y, i * N + j, 0);
    }
    }
    }
    else
    {
    add(i * N + j, 1 , a);
    add(1, i * N + j, 0);
    }
    }
    return 1;
    }
    int bfs()
    {
    int i, j, rear;
    memset(d, -1, sizeof(d));
    d[0] = 0;
    rear = 0;
    q[rear ++] = 0;
    for(i = 0; i < rear; i ++)
    for(j = first[q[i]]; j != -1; j = next[j])
    if(flow[j] && d[v[j]] == -1)
    {
    d[v[j]] = d[q[i]] + 1;
    if(v[j] == 1)
    return 1;
    q[rear ++] = v[j];
    }
    return 0;
    }
    int dinic()
    {
    int i, res = 0, r, cur;
    while(bfs())
    {
    r = cur = 0;
    memcpy(work, first, sizeof(first));
    for(;;)
    {
    if(cur == 1)
    {
    int a = INF, minr = 0;
    for(i = 0; i < r; i ++)
    if(flow[s[i]] < a)
    {
    a = flow[s[i]];
    minr = i;
    }
    for(i = 0; i < r; i ++)
    {
    flow[s[i]] -= a;
    flow[s[i] ^ 1] += a;
    }
    res += a;
    r = minr;
    cur = u[s[r]];
    }
    for(i = work[cur]; i != -1; i = next[i])
    {
    if(flow[i] == 0)
    continue;
    if(d[v[i]] == d[cur] + 1)
    break;
    }
    work[cur] = i;
    if(i != -1)
    {
    s[r ++] = i;
    cur = v[i];
    }
    else
    {
    d[cur] = -1;
    if(r == 0)
    break;
    r --;
    cur = u[s[r]];
    }
    }
    }
    return res;
    }
    int main()
    {
    while(init())
    {
    int res = dinic();
    printf("%d\n", SUM - res);
    }
    return 0;
    }


  • 相关阅读:
    休息一下
    把细节放大街上好孕妇有好多
    在银行钱是这样取的
    Word2003表格的AutoFormatType和Style的兼容问题
    [zz]Boost智能指针——shared_ptr
    thrift 安装运行错误 解决
    [zz]boost/shared_ptr 用法总结
    [zz]理解复杂的C/C++声明 const, typedef , 函数指针(转贴)
    [zz]使用thrift做c++,java和python的相互调用
    [zz]Apache Thrift学习小记
  • 原文地址:https://www.cnblogs.com/staginner/p/2212508.html
Copyright © 2011-2022 走看看