HRBUST

来源:互联网 发布:漱口水 酒精 知乎 编辑:程序博客网 时间:2024/04/29 23:47


棋盘取数Time Limit: 1000 MSMemory Limit: 32768 KTotal Submit: 63(20 users)Total Accepted: 16(9 users)Rating: Special Judge: NoDescription

给你一个n*n的格子的棋盘,每个格子里面有一个非负数。现在从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。

Input

包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数x(n<=20, 0 <= x <= 1000)。

Output

对于每个测试实例,输出可能取得的最大的和。

Sample Input

3

258 83 905

874 941 662

733 415 890

Sample Output

3727

Source2014暑假集训练习赛(8月13日)

网上题解整理(刚入网络流的小白):

看了题解,才明白的:

这个题由于数据范围较大,所以状态压缩过不去,需要用网络流,我重复一遍人家的建图:

我们知道对于普通二分图来说,最大独立点集 + 最小点覆盖集 = 总点数,类似的,对于有权的二分图来说,有:

最大点权独立集 + 最小点权覆盖集 = 总点权和,

这个题很明显是要求 最大点权独立集 ,现在 总点权 已知,我们只要求出来 最小点权覆盖集 就好了,我们可以这样建图,

1,对矩阵中的点进行黑白着色(相邻的点颜色不同),从源点向黑色的点连一条边,权值为该黑色点的权值,

2,从白色的点向汇点连一条边,权值为该白色点的权值,

3,然后,对于每一对相邻的黑白点,从黑点向白点连一条边,权值为无穷大。

最后求最小割(最大流),即为最小点权覆盖集。

因为我们求出的最小割集一定是从那些相邻的黑白点之间的边(也就是不能用的边,因为相邻的数不能同时选)中选出来的,且是最小代价,也就是说从方格中拿掉的数之和尽量小,那么剩下的数之和一定是最大的。

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

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

这题可以用类似国际象棋黑白棋的方法解,每个点和它周围四个点连一条容量为无穷的边,源点和黑点(奇数点)相连,汇点和白点(偶数点)相连,剩下的就看你怎么割了,应该是这么割的:割掉最少最小的边,使得从源点的流量一点都不能到达汇点 ,等价于求源点到汇点的最大流。



  二分图最小点覆盖和最大独立集都可以转化为最大匹配求解。在这个基础上,把每个点赋予一个非负的权值,这两个问题就转化为:二分图最小点权覆盖和二分图最大点权独立集。

 

    二分图最小点权覆盖

    从x或者y集合中选取一些点,使这些点覆盖所有的边,并且选出来的点的权值尽可能小。

建模:

    原二分图中的边(u,v)替换为容量为INF的有向边(u,v),设立源点s和汇点t,将s和x集合中的点相连,容量为该点的权值;将y中的点同t相连,容量为该点的权值。在新图上求最大流,最大流量即为最小点权覆盖的权值和。

 

二分图最大点权独立集

    在二分图中找到权值和最大的点集,使得它们之间两两没有边。其实它是最小点权覆盖的对偶问题。答案=总权值-最小点覆盖集。具体证明参考胡波涛的论文。



在二分图(非二分图,我不确定)中。

最大点权独立集 = 所有点权和-最小点权覆盖集

最小点权覆盖集 = 最小割

而在图论中,最小割 = 最大流。

问题转化为:求s->t最小割(一组权值和最小的割边集,去掉后s->t不连通),而每去掉一条割边,相当于去掉原图一个点,这个点必然牵着下面X->Y的边,故最小割即为最小点权覆盖集!(部分证明:摘自某大牛:可以这样理解:X到Y的边权为INF,自然不会成为最小割中的边,那就只有可能是S到X和Y到T中的边,而:S到X中点x的边e1, 权为点x的点权,点x和Y中的所有临边e2,都需要受到e1的流量的限制,同样,X到Y中点y的所有边也会受到点y到T的容量限制。这样求得割就能保证覆盖掉所有的边。我们可以用反证法证明一下:假设有边<x, y>没有被覆盖掉,则边<S, x>流量为0且边<y, T>流量为0,而<x, y>流量为INF,自然可以找到一条S到T的增流路径<S, x, y, T>,与以求得流为最大流相矛盾,则可以说明,在最大流的情况下,所有的边都已经被覆盖掉。


#include <iostream>#include <cstring>#include <cstdio>#include <algorithm>#include <queue>using namespace std;const int INF = 1e9;const int maxn = 55;const int maxv = 2505;int head[maxv], cur[maxv], d[maxv], n, m, s, t, k, sum;int a[maxn][maxn], dir[4][2] = {0,1,0,-1,1,0,-1,0};struct node{    int v, w, next;}edge[maxv*maxv/2];void addEdge(int u, int v, int w){    edge[k].v = v;    edge[k].w = w;    edge[k].next = head[u];    head[u] = k++;    edge[k].v = u;    edge[k].w = 0;    edge[k].next = head[v];    head[v] = k++;}int bfs(){    memset(d, 0, sizeof(d));    d[s] = 1;    queue<int> q;    q.push(s);    while(!q.empty())    {        int u = q.front();        if(u == t) return 1;        q.pop();        for(int i = head[u]; i != -1; i = edge[i].next)        {            int to = edge[i].v, w = edge[i].w;            if(w && d[to] == 0)            {                d[to] = d[u] + 1;                if(to == t) return 1;                q.push(to);            }        }    }    return 0;}int dfs(int u, int maxflow){    if(u == t) return maxflow;    int ret = 0;    for(int i = cur[u]; i != -1; i = edge[i].next)    {        int to = edge[i].v, w = edge[i].w;        if(w && d[to] == d[u]+1)        {            int f = dfs(to, min(maxflow-ret, w));            edge[i].w -= f;            edge[i^1].w += f;            ret += f;            if(ret == maxflow) return ret;        }    }    return ret;}int Dinic(){    int ans = 0;    while(bfs() == 1)    {        memcpy(cur, head, sizeof(head));        ans += dfs(s, INF);    }    return ans;}int main(){    while(~scanf("%d%d", &n, &m))    {        s = 0, t = n*m+1, k = 0, sum = 0;        memset(head, -1, sizeof(head));        for(int i = 1; i <= n; i++)            for(int j = 1; j <= m; j++)                scanf("%d", &a[i][j]), sum += a[i][j];        for(int i = 1; i <= n; i++)            for(int j = 1; j <= m; j++)            {                if((i+j)%2 == 0)                {                    addEdge(s, (i-1)*m+j, a[i][j]);                    for(int x = 0; x < 4; x++)  //这一块一定在%2里面, 只需要从是偶数的往奇数建边就好了, 其余的就不用了                    {                        int tx = i + dir[x][0];                        int ty = j + dir[x][1];                        if(tx < 1 || ty < 1 || tx > n || ty > m) continue;                        addEdge((i-1)*m+j, (tx-1)*m+ty, INF);                    }                }                else                    addEdge((i-1)*m+j, t, a[i][j]);            }//        cout << Dinic() << endl;        printf("%d\n", sum-Dinic());    }    return 0;}


原创粉丝点击