B. 方格取数
时间限制(普通/Java) :
3000 MS/ 6000 MS 运行内存限制 : 65535 KByte
总提交 : 77 测试通过 : 18
总提交 : 77 测试通过 : 18
比赛描述
在一个有m*n个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意2个数所在方格没有公共边,且取出的数的总和最大。
输入
第1行有2个正整数m和n,分别表示棋盘的行数和列数。接下来的m行,每行有n个正整数,表示棋盘方格中的数。(1<=m,n<=30,答案保证在int范围内)
输出
最大总和。
样例输入
3 3
1 2 3
3 2 3
2 3 1
样例输出
11
提示
题目来源
2012南京邮电大学第四届大学生程序设计大赛(热身赛)
/*
定理:
1、最大点权独立集 = sum - 最小点权覆盖集。
2、最小点权覆盖集 = 最小割 = 最大流
实现:dinic算法
*/
/* Internet 5MS
#include <iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
const int nMax = 2505;
const int INF = 0x7fffffff;
int queue[nMax];//建立层次图时使用到的队列
int dis[nMax];//各节点在层次图中对应的层次数
struct Edge
//邻接表,包括:边的起点、边的权值、起点相同的下一条边
{
int v, w, next;
Edge() {}
Edge(int v, int w, int next):v(v), w(w), next(next) {}
} adj[8 * nMax];
int V[nMax];//V[u]表示起点为u的第一条边,与Edge结合使用,从而实现邻接表的效果
int cnt;
int s, t;
int min(int a, int b)
{
return a < b ? a : b;
}
void add(int u, int v, int w)//向邻接表中添加 u - > v 结构
{
adj[cnt] = Edge(v, w, V[u]);
V[u] = cnt ++;
adj[cnt] = Edge(u, 0, V[v]);
V[v] = cnt ++;
}
int bfs()//建层次图
{
int front, rear;
int v;
memset(dis, 0, sizeof(dis));
front = rear = 0;
dis[s] = 1;
queue[front ++] = s;
while(rear < front)
{
int u = queue[rear ++];
for(int i = V[u]; i != -1; i = adj[i].next)//与u相连的边
if(adj[i].w && dis[v = adj[i].v] == 0)//可通行并且 v 之间没有被访问过
{
dis[v] = dis[u] + 1;
if(v == t) return 1;
queue[front ++] = v;
}
}
return 0;
}
int dfs(int u, int limit = INF)//返回从u出发到t,增广路经的最小边
{
if(u == t) return limit;
int count = 0;
for(int i = V[u]; i != -1; i = adj[i].next)//与u 相连的边
{
int v = adj[i].v;
if((dis[v] == dis[u] + 1) && adj[i].w)//根据层次的关系,找到的路径就为最短路径
{
int z = dfs(v, min(limit - count, adj[i].w));
if(z > 0)//增广路经的最小边不为0,即v到t可通行
{
count += z;
adj[i].w -= z;
adj[i ^ 1].w += z;//改为adj[i + 1] += z , 会超时!
}
else
dis[v] = -1;//效果等同于删除与v相关的所有边
}
}
return count;
}
int dinic()
{
int ans = 0;
while(bfs())//直到搜索不到增广路经为止
ans += dfs(s);
return ans;
}
void init()
{
cnt = 0;
memset(V, -1, sizeof(V));
}
int main()
{
int m, n;
int sum;
while(scanf("%d %d", &m, &n) != EOF)
{
init();
int x;
sum = 0;
s = 0;
t = m * n + 1;
for(int i = 1; i <= m; ++ i)
for(int j = 1; j <= n; ++ j)
{
scanf("%d", &x);
sum += x;
if((i + j) & 1)
{
add(s, (i - 1) * n + j, x);
//上
if(i > 1) add((i - 1) * n + j, (i - 2) * n + j, INF);
//下
if(i < m) add((i - 1) * n + j, i * n + j, INF);
//左
if(j > 1) add((i - 1) * n + j, (i -1) * n + j - 1, INF);
//右
if(j < n) add((i - 1) * n + j, (i - 1) * n + j + 1, INF);
}
else
add((i - 1) * n + j, t, x);
}
printf("%d\n",sum - dinic());
}
return 0;
}
*/
/* 1MS 5MS OJ也不固定
#include <iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
const int nMax = 2505;
const int INF = 0x7fffffff;
int queue[nMax];//建立层次图时使用到的队列
int dis[nMax];//各节点在层次图中对应的层次数
struct Edge{//邻接表,包括:边的起点、边的权值、起点相同的下一条边
int v, w, next;
Edge() {}
Edge(int v, int w, int next):v(v), w(w), next(next) {}
}adj[8 * nMax];
int V[nMax];//V[u]表示起点为u的第一条边,与Edge结合使用,从而实现邻接表的效果
int cnt;
int s, t;
void add(int u, int v, int w){//向邻接表中添加 u - > v 结构
adj[cnt] = Edge(v, w, V[u]); // 插在邻接表的表头
V[u] = cnt++;
adj[cnt] = Edge(u, 0, V[v]); // v->u 的权重为0 表示反向不通
V[v] = cnt++;
}
int bfs(){//建层次图
int front, rear;
int v;
memset(dis, 0, sizeof(dis));
front = rear = 0;
dis[s] = 1;
queue[front ++] = s;
while(rear < front){
int u = queue[rear ++];
for(int i = V[u]; i != -1; i = adj[i].next){//与u相连的边
if(adj[i].w && dis[v=adj[i].v] == 0){//可通行并且 v 之间没有被访问过
dis[v] = dis[u] + 1;
if(v == t){
return 1;
}
queue[front++] = v;
}
}
}
return 0;
}
int dfs(int u, int limit = INF){//返回从u出发到t,增广路经的最小边
if(u == t){
return limit;
}
int count = 0;
for(int i = V[u]; i != -1; i = adj[i].next){//与u 相连的边
int v = adj[i].v;
if((dis[v] == dis[u] + 1) && adj[i].w){//根据层次的关系,找到的路径就为最短路径
int z = dfs(v, min(limit - count, adj[i].w));
if(z > 0){//增广路经的最小边不为0,即v到t可通行
count += z;
adj[i].w -= z;
adj[i^1].w += z;//边是连续存储的,例如adj[1]的反向边是adj[2]
}else{
dis[v] = -1;//删除与v相关的所有边,因为 v 已经不可以到达t了
}
}
}
return count;
}
int dinic(){
int ans = 0;
while(bfs()){
ans += dfs(s);
}
return ans;
}
void init(){
cnt = 0;
memset(V, -1, sizeof(V));
}
int getNum(){
int t,r;
while((t=getchar())<'0' || t>'9');
r = t-'0';
while((t=getchar())>='0' && t<='9'){
r = r*10 + t-'0';
}
return r;
}
int main(){
int m, n;
int sum;
while(scanf("%d %d", &m, &n) != EOF){
init();
int x;
sum = 0;
s = 0;
t = m * n + 1;
for(int i = 1; i <= m; ++ i){
for(int j = 1; j <= n; ++ j){
// scanf("%d", &x);
x = getNum();
sum += x;
if((i+j)&1){
add(s, (i-1)*n+j, x);
if(i > 1){//上
add((i-1)*n+j, (i-2)*n+j, INF);
}
if(i < m){//下
add((i-1)*n+j, i*n+j, INF);
}
if(j > 1){//左
add((i-1)*n+j, (i-1)*n+j-1, INF);
}
if(j < n){//右
add((i-1)*n+j, (i-1)*n+j+1, INF);
}
}
else{
add((i-1)*n+j, t, x);
}
}
}
printf("%d\n",sum-dinic());
}
return 0;
}
*/
/*
最大点权独立集:
转化为最小点权覆盖问题,最大点权独立集=总权值-最小点权覆盖集
最小点权覆盖:
设立源点s和t,s连边到点i,容量为i点的权值;点j连边到t,容量为j点权值;原二分图中的边容量为INF,求最大流即为最小点权覆盖。
*/
/* Internet 1MS 4MS
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 0x7fffffff;
const int maxv = 1000;
const int maxe = 10000;
int n,m;
int g[31][31];
struct Edge
{
int v;
int next;
int flow;
};
Edge e[maxe];
int head[maxv],edgeNum;
int now[maxv],d[maxv],vh[maxv],pre[maxv],preh[maxv];
int start,end;
void addEdge(int a,int b,int c)
{
e[edgeNum].v = b;
e[edgeNum].flow = c;
e[edgeNum].next = head[a];
head[a] = edgeNum++;
e[edgeNum].v = a;
e[edgeNum].flow = 0;
e[edgeNum].next = head[b];
head[b] = edgeNum++;
}
void Init()
{
edgeNum = 0;
memset(head,-1,sizeof(head));
memset(d,0,sizeof(d));
}
int sap(int s,int t,int n) //源点,汇点,结点总数
{
int i,x,y;
int f,ans = 0;
for(i = 0; i < n; i++)
now[i] = head[i];
vh[0] = n;
x = s;
while(d[s] < n)
{
for(i = now[x]; i != -1; i = e[i].next)
if(e[i].flow > 0 && d[y=e[i].v] + 1 == d[x])
break;
if(i != -1)
{
now[x] = preh[y] = i;
pre[y] = x;
if((x=y) == t)
{
for(f = INF,i=t; i != s; i = pre[i])
if(e[preh[i]].flow < f)
f = e[preh[i]].flow;
ans += f;
do
{
e[preh[x]].flow -= f;
e[preh[x]^1].flow += f;
x = pre[x];
}while(x!=s);
}
}
else
{
if(!--vh[d[x]])
break;
d[x] = n;
for(i=now[x]=head[x]; i != -1; i = e[i].next)
{
if(e[i].flow > 0 && d[x] > d[e[i].v] + 1)
{
now[x] = i;
d[x] = d[e[i].v] + 1;
}
}
++vh[d[x]];
if(x != s)
x = pre[x];
}
}
return ans;
}
void build()
{
int i,j;
for(i = 1; i <= m; i++)
{
for(j = 1; j <= n; j++)
{
int t = (i-1)*n+j;
if((i+j)%2)
{
addEdge(start,t,g[i][j]);
if(i>1)
addEdge(t,t-n,INF);
if(i<m)
addEdge(t,t+n,INF);
if(j>1)
addEdge(t,t-1,INF);
if(j<n)
addEdge(t,t+1,INF);
}
else
addEdge(t,end,g[i][j]);
}
}
}
int getNum(){
int t,r;
while((t=getchar())<'0' || t>'9');
r = t-'0';
while((t=getchar())>='0' && t<='9'){
r = r*10 + t-'0';
}
return r;
}
int main()
{
int i,j;
int result;
while(scanf("%d %d",&m,&n) != EOF)
{
result = 0;
Init();
for(i = 1; i <= m; i++)
{
for(j = 1; j <= n; j++)
{
// scanf("%d",&g[i][j]);
g[i][j] = getNum();
result += g[i][j];
}
}
start = 0;
end = n*m + 1;
build();
printf("%d\n",result-sap(start,end,end+1));
}
return 0;
}
*/
nclude <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 0x7fffffff;
const int maxv = 1000;
const int maxe = 10000;
int n,m;
int g[31][31];
struct Edge
{
int v;
int next;
int flow;
};
Edge e[maxe];
int head[maxv],edgeNum;
int now[maxv],d[maxv],vh[maxv],pre[maxv],preh[maxv];
int start,end;
void addEdge(int a,int b,int c)
{
e[edgeNum].v = b;
e[edgeNum].flow = c;
e[edgeNum].next = head[a];
head[a] = edgeNum++;
e[edgeNum].v = a;
e[edgeNum].flow = 0;
e[edgeNum].next = head[b];
head[b] = edgeNum++;
}
void Init()
{
edgeNum = 0;
memset(head,-1,sizeof(head));
memset(d,0,sizeof(d));
}
int sap(int s,int t,int n) //源点,汇点,结点总数
{
int i,x,y;
int f,ans = 0;
for(i = 0; i < n; i++)
now[i] = head[i];
vh[0] = n;
x = s;
while(d[s] < n)
{
for(i = now[x]; i != -1; i = e[i].next)
if(e[i].flow > 0 && d[y=e[i].v] + 1 == d[x])
break;
if(i != -1)
{
now[x] = preh[y] = i;
pre[y] = x;
if((x=y) == t)
{
for(f = INF,i=t; i != s; i = pre[i])
if(e[preh[i]].flow < f)
f = e[preh[i]].flow;
ans += f;
do
{
e[preh[x]].flow -= f;
e[preh[x]^1].flow += f;
x = pre[x];
}while(x!=s);
}
}
else
{
if(!--vh[d[x]])
break;
d[x] = n;
for(i=now[x]=head[x]; i != -1; i = e[i].next)
{
if(e[i].flow > 0 && d[x] > d[e[i].v] + 1)
{
now[x] = i;
d[x] = d[e[i].v] + 1;
}
}
++vh[d[x]];
if(x != s)
x = pre[x];
}
}
return ans;
}
void build()
{
int i,j;
for(i = 1; i <= m; i++)
{
for(j = 1; j <= n; j++)
{
int t = (i-1)*n+j;
if((i+j)%2)
{
addEdge(start,t,g[i][j]);
if(i>1)
addEdge(t,t-n,INF);
if(i<m)
addEdge(t,t+n,INF);
if(j>1)
addEdge(t,t-1,INF);
if(j<n)
addEdge(t,t+1,INF);
}
else
addEdge(t,end,g[i][j]);
}
}
}
int getNum(){
int t,r;
while((t=getchar())<'0' || t>'9');
r = t-'0';
while((t=getchar())>='0' && t<='9'){
r = r*10 + t-'0';
}
return r;
}
int main()
{
int i,j;
int result;
while(scanf("%d %d",&m,&n) != EOF)
{
result = 0;
Init();
for(i = 1; i <= m; i++)
{
for(j = 1; j <= n; j++)
{
// scanf("%d",&g[i][j]);
g[i][j] = getNum();
result += g[i][j];
}
}
start = 0;
end = n*m + 1;
build();
printf("%d\n",result-sap(start,end,end+1));
}
return 0;
}