题目链接:hdoj 1565 方格取数(1)
Dinic模板:hdoj 3549 FlowProblem(Dinic+当前弧优化 模板)
题意:在方格中取数,不能取到相邻的方格,求取数最大和。
解题思路:
题中对取数条件进行了约束,不能取相邻的数,那么这种约束可以通过网络流边上的容量来刻画,再用最大流最小割定理解决。
解题过程:
1、根据点的行+列的奇偶性进行染色,比如将行+列为奇数的染成黑色,行+列为奇数的染成白色。将黑色点与源点S连边,边的容量为点权;将白点与源点T连边,边的容量为点权;将黑色点与它周围的白色点连边,边的容量为∞,代表这条边不进入割。
2、题目要求取数最大值,也等于总值 - 不取的数的最小值,也就将问题转换成了求最小割。若黑色点B1与源点S相连,白色点W1与源点T相连,B1与W1相连,那么B1和W1不能同时取到,也就是说,B1与S的边 和 W1与T的边,必定有一个要进入割。
3、根据最大流最小割定理,最小割在数值上与最大流相同,求出最大流即可。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<sstream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<utility>
#define JOJO cout<<"JOJO"<<endl;
using namespace std;
const long long INF = ~0ull>>2;
const int Inf = 0x3f3f3f3f;
const int maxn = 5e2+10;
int head[maxn], cur[maxn];
int lev[maxn];
int S = 0, T; //S代表源点,T代表汇点
int t;
struct tagedge{
int to, next;
long long cap;
}edge[maxn*maxn/4];
bool LevelGraph()
{
queue<int> q;
fill(lev, lev+maxn, -1);
lev[S] = 0;
q.push(S);
while (!q.empty()){
int k = q.front();
q.pop();
for (int i = head[k]; i != -1; i = edge[i].next){
int v = edge[i].to;
if (lev[v] == -1 && edge[i].cap > 0){
lev[v] = lev[k] + 1;
q.push(v);
}
}
}
if (lev[T] == -1)
return false;
else
return true;
}
int FindPath(int k, long long flow_in)
{
if (k == T)
return flow_in;
long long flow_out = 0;
for (int i = cur[k]; i != -1; i = edge[i].next){
cur[k] = i;
int v = edge[i].to;
if (lev[v] == lev[k]+1 && edge[i].cap > 0){
long long flow = FindPath(v, min(flow_in, edge[i].cap));
if (flow == 0) continue;
flow_in -= flow;
flow_out += flow;
edge[i].cap -= flow;
edge[i^1].cap += flow;
if (flow_in == 0) break;
}
}
return flow_out;
}
long long Dinic()
{
long long res = 0;
while (LevelGraph()){
memcpy(cur, head, sizeof(head));
res += FindPath(0, Inf);
}
return res;
}
void AddEdge(int u, int v, long long w)
{
edge[t].to = v;
edge[t].cap = w;
edge[t].next = head[u];
head[u] = t++;
return;
}
void init()
{
fill(head, head+maxn, -1);
t = 0;
return;
}
int main()
{
int n;
while (scanf("%d", &n) != EOF){
init();
T = n*n+5;
long long sum = 0;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
long long a;
scanf("%lld", &a);
sum += a;
if ((i+j)%2){
AddEdge(S, (i-1)*n+j, a);
AddEdge((i-1)*n+j, S, 0);
if (i > 1){
AddEdge((i-1)*n+j, (i-2)*n+j, INF);
AddEdge((i-2)*n+j, (i-1)*n+j, 0);
}
if (i < n){
AddEdge((i-1)*n+j, i*n+j, INF);
AddEdge(i*n+j, (i-1)*n+j, 0);
}
if (j > 1){
AddEdge((i-1)*n+j, (i-1)*n+j-1, INF);
AddEdge((i-1)*n+j-1, (i-1)*n+j, 0);
}
if (j < n){
AddEdge((i-1)*n+j, (i-1)*n+j+1, INF);
AddEdge((i-1)*n+j+1, (i-1)*n+j, 0);
}
}
else{
AddEdge((i-1)*n+j, T, a);
AddEdge(T, (i-1)*n+j, 0);
}
}
}
printf("%lld\n", sum-Dinic());
}
return 0;
}