比赛描述
在一个有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; }