zoukankan      html  css  js  c++  java
  • 【TYVJ】1338 QQ农场(最大流+最大权闭合图)

    http://tyvj.cn/Problem_Show.aspx?id=1338

    时间才排到rank7,还不快啊囧。isap我常数都写得那么小了。。。

    最大权闭合图看我另一篇博文吧

    此题很明显的模型。

    首先我们先染色,使整个图黑白相间,其中我们只需要在黑色点向对应的上下左右白色节点连边,很明显,这些节点都有权值,我们需要求的是最大权,那么就按我的博文那样来做即可。

    upd:本题并不是裸的最大权闭合图模型,而是最小割模型,即我们将问题转换为:去掉“同时取冲突的格子”的情况得到“不冲突”。即最小化“同时取冲突的格子”,将所有格子放到一个流网络,其中s连接白(黑)格子,t连接黑(白)格子,相邻的黑白格子连接容量oo,那么一个最小割[s, t]对应一个简单割,且简单割对应一种方案。

    用sum-最小割就是答案。

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <string>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define rep(i, n) for(int i=0; i<(n); ++i)
    #define for1(i,a,n) for(int i=(a);i<=(n);++i)
    #define for2(i,a,n) for(int i=(a);i<(n);++i)
    #define for3(i,a,n) for(int i=(a);i>=(n);--i)
    #define for4(i,a,n) for(int i=(a);i>(n);--i)
    #define CC(i,a) memset(i,a,sizeof(i))
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define read(a) a=getnum()
    #define print(a) printf("%d", a)
    inline int getnum() { int ret=0; char c; for(c=getchar(); c<'0' || c>'9'; c=getchar()); for(; c>='0' && c<='9'; c=getchar()) ret=ret*10+c-'0'; return ret; }
    
    const int N=40005, M=N*4*2, oo=~0u>>2;
    int ihead[N], inext[M], from[M], to[M], cap[M], cnt=1;
    int gap[N], d[N], cur[N], p[N];
    
    inline void add(const int &u, const int &v, const int &w) {
    	inext[++cnt]=ihead[u]; ihead[u]=cnt; from[cnt]=u; to[cnt]=v; cap[cnt]=w;
    	inext[++cnt]=ihead[v]; ihead[v]=cnt; from[cnt]=v; to[cnt]=u; cap[cnt]=0;
    }
    
    inline int isap(const int &s, const int &t, const int &n) {
    	int u=s, f, i, flow=0;
    	for1(i, 0, n) cur[i]=ihead[i];
    	gap[0]=n;
    	while(d[s]<n) {
    		for(i=cur[u]; i; i=inext[i]) if(cap[i] && d[u]==d[to[i]]+1) break;
    		if(i) {
    			cur[u]=i; p[to[i]]=i; u=to[i];
    			if(u==t) {
    				for(f=oo; u!=s; u=from[p[u]]) f=min(f, cap[p[u]]);
    				for(u=t; u!=s; u=from[p[u]]) cap[p[u]]-=f, cap[p[u]^1]+=f;
    				flow+=f;
    			}
    		}
    		else {
    			if(!(--gap[d[u]])) break;
    			d[u]=n;
    			cur[u]=ihead[u];
    			for(i=ihead[u]; i; i=inext[i]) if(cap[i] && d[u]>d[to[i]]+1) 
    				d[u]=d[to[i]]+1;
    			++gap[d[u]];
    			if(u!=s) u=from[p[u]];
    		}
    	}
    	return flow;
    }
    
    int main() {
    	int t, u, n;
    	read(n);
    	int S=0, T=n*n+1;
    	int sum=0, last;
    	for1(i, 1, n) {
    		last=i;
    		for1(j, 1, n) {
    			read(t); sum+=t;
    			u=(i-1)*n+j;
    			if(last%2) {
    				if(i>1) add(u, u-n, oo);
    				if(i<n) add(u, u+n, oo);
    				if(j>1) add(u, u-1, oo);
    				if(j<n) add(u, u+1, oo);
    				add(S, u, t);
    			}
    			else add(u, T, t);
    			++last;
    		}
    	}
    	printf("%d
    ", sum-isap(S, T, T+1));
    	
    	return 0;
    }
    

    背景 Background

    Sandytea前段时间沉迷于QQ农场中……一天夜里,他梦见来到好友X的农场上……

    描述 Description

    这个农场和游戏中略有不同。土地实际上是一个边长为N的正方形,由N*N块土地组成。
    在每块土地上,都种有一种农作物。如果他选择摘取一块土地上的农作物,就能获得一个固定的利润(当然,这个利润是正数)。不同土地上的利润多半是不同的。
    贪心的Sandytea本想摘取所有土地上的农作物。但是正当他准备行动时,却被告知不允许摘取了两块有公共边的土地上的作物,否则就会被主人的狗发现。
    Sandytea想知道,在不被狗抓住的前提下,他能获得的最大利益是多少。

    输入格式 InputFormat

    第一行:一个整数N,表示土地是一个边长为N的正方形。
    下面N行:每行N个正整数,描述了各块土地上的农作物的单位价值。

    输出格式 OutputFormat

    输出一行,包含一个整数,为最大的收益。

    样例输入 SampleInput [复制数据]

    2
    7 7
    54 54

    样例输出 SampleOutput [复制数据]

    61

    数据范围和注释 Hint

    数据范围:
    有10分的数据满足:N≤6
    另有20分的数据满足:N≤13
    另有30分的数据满足:N≤50
    另有40分的数据满足:N≤200
    所有数据满足:每块土地上作物的价值不超过100。

    来源 Source

    改编自SPOJ
  • 相关阅读:
    MS SQL float转nvarchar
    RDLC添加页码
    RDLC报表分页显示标题
    程序员必读书单
    SQL查询结果增加序号列
    SQL字符串分组聚合(分组后的数据查询后用逗号隔开)
    现在有一列是int的,把它变成5位的字符串,左边不够的补0
    oracle客户端精简绿色版-环境变量配置
    DISTINCT后按照DISTINCT之前的某列进行排序
    设计模式之代理模式
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/3893519.html
Copyright © 2011-2022 走看看