南邮 OJ 1884 B. 方格取数

B. 方格取数

时间限制(普通/Java) :  3000 MS/ 6000 MS          运行内存限制 : 65535 KByte
总提交 : 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值