题目链接
首先说一下,这题和P1361 小M的作物 很像,同样的做题思路
题目描述
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
输入格式
第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数。接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数。
输出格式
程序运行结束时,将取数的最大总和输出
输入输出样例
input
3 3
1 2 3
3 2 3
2 3 1
output
11
分析:
和[P1361 小M的作物]这题差不多,都是先需要在这个方格里面看是否有方格数之间存在某种联系,发现确实是存在的,根据题意,发现奇数点永远与偶数点是连接的,那么这样就可以将奇数点与偶数点相连接,可以设为INF,也可以设为S到奇数点与偶数点到T的和(当然,反过来也是可以的)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<cmath>
#include<cstring>
#include<set>
#include<queue>
#include<stack>
#include<map>
typedef long long ll;
using namespace std;
const int INF=0x3f3f3f3f;
struct node{
int from,to,cap,flow;
};
vector<int>G[50000];
vector<node>edge;
void add(int from ,int to,ll cap){
edge.push_back((node){from,to,cap,0});
edge.push_back((node){to,from,0,0});
int m=edge.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
int s,t;
bool vis[50000];
int d[50000],cur[50000];
bool bfs(){
memset(vis,0,sizeof vis);
queue<int>q;
q.push(s);
d[s]=0;
vis[s]=1;
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<G[x].size();i++){
node& e=edge[G[x][i]];
if(!vis[e.to] && e.cap>e.flow){
vis[e.to]=1;
d[e.to]=d[x]+1;
q.push(e.to);
}
}
}
return vis[t];
}
int dfs(int x,int a){
if(x==t|| a==0) return a;
int flow=0,f;
for(int & i=cur[x]; i< G[x].size(); i++){
node & e = edge[G[x][i]];
if(d[x]+1==d[e.to] && (f=(dfs(e.to,min(a,e.cap-e.flow))))>0){
e.flow+= f;
edge[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0) break;
}
}
return flow;
}
int dinic(){
int flow=0;
memset(d,0,sizeof d);
while(bfs()){
memset(cur,0,sizeof cur);
flow+=dfs(s,INF);
}
return flow;
}
int e[220][220];
int n,m;
bool check(int x,int y){
if(x>n||y>m||x<1||y<1) return false;
return true;
}
int dir[4][2]={{0,-1},{0,1},{1,0},{-1,0}};
int point(int x,int y){
return (x-1) *m+y;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif // ONLINE_JUDGE
scanf("%d%d",&n,&m);
s=0;t=n*m+1;
int sum=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&e[i][j]);
sum+=e[i][j];
if((i+j)&1)
add(s,point(i,j),e[i][j]);
else
add(point(i,j),t,e[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if((i+j)&1){//这里就是奇数点的意思
for(int k=0;k<4;k++){
int dx=i+dir[k][0],dy=j+dir[k][1];
if(check(dx,dy)){
add(point(i,j),point(dx,dy),e[i][j]+e[dx][dy]);//这里设成INF也可以
}
}
}
// else{//下面这个else可不加,因为再建一条是不会影响到最小割的大小的,所以不会影响到最小割的值
// for(int k=0;k<4;k++){
// int dx=i+dir[k][0],dy=j+dir[k][1];
// if(check(dx,dy)){
// add(point(dx,dy),point(i,j),e[i][j]+e[dx][dy]);
// }
// }
// }
}
}
printf("%d\n",sum-dinic());
return 0;
}
综上所述,遇见这种方格数目的匹配问题的时候,可以用最小割来去解决,分析一下奇数点和偶数点之间是否存在什么关系可以用于建图