hdu 1565 最小点权覆盖(最大流EK)
/* 题意:中文题 题解:求最小权点覆盖集; 参考网站:http://yzmduncan.iteye.com/blog/1149057 最小权点覆盖=总权值-最大流 题目要求所选两个方格不能有共边,因此将所有的方格看作顶点,公共边看作连接两个顶点的边,这样 这个方形就转变为了一个无向图;题目要求两两顶点间没有公共边,即通过去除部分顶点来去除所有的 边,剩下的没有边的图即为没有公共边的所有方格,然后保证剩下的权值和最大,亦即求最小权点覆盖 集的权值之和; 建图方法:首先确定该图是一个二分图,然后在此基础上构建有向图,将二分图的两个集合x,y找出, x和y之间的(u,v)权值变为INF,方向u->v;加入一个起点s和汇点t;s指向x的所有点,y的所有点指 向t,边的权值为该点的值。 然后求出最大流,用总和减去最大流即为最终解。 注意:在建图的时候反向边依旧需要添加。 */ #include <iostream> #include <cstring> #include <queue> #define EMAX 3000 #define VMAX 405 using namespace std; const int INF = 0x7fffffff; int dir[4][2]={0, 1, 0, -1, 1, 0, -1, 0}; int EN;//边的总数 int head[VMAX];//用邻接表表示图 int pre[VMAX];//记录路径 int path[EMAX];//记录该点边的位置 struct edge { int to; int weight;//流量 int next; }e[EMAX]; void insert(int u, int v, int w) { e[EN].next = head[u]; e[EN].to = v; e[EN].weight = w; head[u] = EN++; e[EN].next = head[v]; e[EN].to = u; e[EN].weight = 0; head[v] = EN++; } int flow_ek(int s, int t) { queue<int> Q; int ret = 0; while (true) { memset(pre,-1,sizeof(pre)); while (!Q.empty()) Q.pop(); Q.push(s); while (!Q.empty())//BFS { int u = Q.front(); Q.pop(); for(int i=head[u]; i!=-1; i=e[i].next) { if (pre[e[i].to]==-1 && e[i].weight>0) { pre[e[i].to] = u;//记录路径 path[e[i].to] = i;//记录该点边的所在位置,方便后面直接查询 Q.push(e[i].to); } } if (pre[t] != -1)//终点的pre不为-1,表示找到一条增广路径 break; } if (pre[t] == -1)//当BFS后找不到增广路径则结束循环 break; int mw = -1; for(int v=t; v!=s; v=pre[v])//找出当前路径中的流量的最小容量 { if (mw == -1 || mw > e[path[v]].weight) mw = e[path[v]].weight; } for(int v=t; v!=s; v=pre[v])//修改路径的容量,求出残余网络 { e[path[v]].weight -= mw; e[path[v]^1].weight += mw;//更新逆向边 } ret += mw; } return ret; } int main(void) { int n,temp; while (cin >> n) { memset(head,-1,sizeof(head)); EN = 0; int sum = 0; for(int i=0; i<n; i++) { for(int j=1; j<=n; j++) { cin >> temp; sum += temp; if ((i+j)%2 == 1)//插入于起点、汇点相连的边 insert(0,i*n+j,temp); else insert(i*n+j,n*n+1,temp); } } for(int i=0; i<n; i++) { for(int j=1; j<=n; j++) { if ((i+j)%2 == 1) { for(int k=0;k<4;k++) { int a=i+dir[k][0]; int b=j+dir[k][1]; if (a>=0&&a<n&&b>=1&&b<=n)//插入二分图之间的有向边 { insert(i*n+j,a*n+b,INF); } } } } } cout << sum - flow_ek(0,n*n+1) << endl; } return 0; }