【bzoj3996】[TJOI2015]线性代数 最大权闭合图

题目描述

给出一个N*N的矩阵B和一个1*N的矩阵C。求出一个1*N的01矩阵A.使得

D=(A*B-C)*A^T最大。其中A^T为A的转置。输出D

输入

第一行输入一个整数N,接下来N行输入B矩阵,第i行第J个数字代表Bij.

接下来一行输入N个整数,代表矩阵C。矩阵B和矩阵C中每个数字都是不超过1000的非负整数。

输出

输出最大的D

样例输入

3
1 2 1
3 1 0
1 2 3
2 3 7

样例输出

2



题解

网络流最大权闭合图

(推导过程什么的不重要,只要注意一下矩阵乘法不满足结合律。其实看懂结论就行)

由于A是01矩阵,所以bij对答案有贡献的前提是ai和aj都为1;而若ai为1,则会对答案产生贡献-ci

即取bij的前提是取-ci和-cj。

很容易看出这是一个最大权闭合图模型。

连边s->pos(bij),容量为bij;pos(ci)->t,容量为ci;pos(bij)->pos(ci)、pos(cj),容量为inf。

然后跑最小割,答案为∑bij-mincut。

MDZZ写个LaTeX真是累死了

#include <cstdio>
#include <cstring>
#include <queue>
#define N 300000
#define M 2000000
#define inf 0x3f3f3f3f
using namespace std;
queue<int> q;
int head[N] , to[M] , val[M] , next[M] , cnt = 1 , s , t , dis[N];
void add(int x , int y , int z)
{
	to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
	to[++cnt] = x , val[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt;
}
bool bfs()
{
	int x , i;
	memset(dis , 0 , sizeof(dis));
	while(!q.empty()) q.pop();
	dis[s] = 1 , q.push(s);
	while(!q.empty())
	{
		x = q.front() , q.pop();
		for(i = head[x] ; i ; i = next[i])
		{
			if(val[i] && !dis[to[i]])
			{
				dis[to[i]] = dis[x] + 1;
				if(to[i] == t) return 1;
				q.push(to[i]);
			}
		}
	}
	return 0;
}
int dinic(int x , int low)
{
	if(x == t) return low;
	int temp = low , i , k;
	for(i = head[x] ; i ; i = next[i])
	{
		if(val[i] && dis[to[i]] == dis[x] + 1)
		{
			k = dinic(to[i] , min(temp , val[i]));
			if(!k) dis[to[i]] = 0;
			val[i] -= k , val[i ^ 1] += k;
			if(!(temp -= k)) break;
		}
	}
	return low - temp;
}
int main()
{
	int n , i , j , x , ans = 0;
	scanf("%d" , &n);
	s = 0 , t = n * n + n + 1;
	for(i = 1 ; i <= n ; i ++ )
		for(j = 1 ; j <= n ; j ++ )
			scanf("%d" , &x) , add(s , (i - 1) * n + j , x) , add((i - 1) * n + j , i + n * n , inf) , add((i - 1) * n + j , j + n * n , inf) , ans += x;
	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &x) , add(i + n * n , t , x);
	while(bfs()) ans -= dinic(s , inf);
	printf("%d\n" , ans);
	return 0;
}
时间: 2024-10-19 07:21:17

【bzoj3996】[TJOI2015]线性代数 最大权闭合图的相关文章

HDU 3061:Battle(最大权闭合图)

http://acm.hdu.edu.cn/showproblem.php?pid=3061 题意:中文题意. 思路:和上一题神似啊,比上一题还简单,重新看了遍论文让我对这个理解更加深了. 闭合图:如果某个点在图中的话,那么这个点的后继点全部都要在图中. 对应至题目,这里的必须攻占b以后才能攻占a,那么是a依赖于b.如果a在图中的话,那么b必定在图中(因为a是依赖于b的),所以是a连向b(而不是b连向a). 这里总结一下做最大权闭合图的套路:把权值为正的点与超级源点S相连,容量为该权值,把权值为

HDU5772 String problem 最大权闭合图+巧妙建图

题意:自己看吧(不是很好说) 分析: 网络流:最大权闭合子图. 思路如下: 首先将点分为3类 第一类:Pij 表示第i个点和第j个点组合的点,那么Pij的权值等于w[i][j]+w[j][i](表示得分) 第二类:原串中的n个点每个点拆出一个点,第i个点权值为 –a[s[i]] (表示要花费) 第三类:对于10种字符拆出10个点,每个点的权值为  -(b[x]-a[x]) 那么我们可以得到一个关系图 ,对于第一类中的点Pij,如果想要选择Pij,你就必须要选中第二类中的点i和j,对于第二类中的点

poj Firing(最大权闭合图)

Firing 题目: 要解雇一些人,而解雇的这些人如果人跟他有上下级的关系,则跟他有关系的人也要一起解雇.每个人都会创造一定的价值,要求你求出在最大的获利下,解雇的人最小. 算法分析: 在这之前要知道一个定理: 最小割 = 最大流 一道最大权闭合图的裸题,而这又可以转换成最小割来求解.证明可以看2007年胡伯涛的论文则可以直接套出模板,没看过的最好去看一下,那里解释的清楚.这里我给出他的原文的一些构造方法. 增加源s汇t 源s连接原图的正权点,容量为相应点权 原图的负权点连接汇t,容量为相应点权

hdu 3061 hdu 3996 最大权闭合图 最后一斩

hdu 3061 Battle :一看就是明显的最大权闭合图了,水提......SB题也不说边数多少....因为开始时候数组开小了,WA....后来一气之下,开到100W,A了.. hdu3996.  gold mine..看了一下,简单题,几乎裸,不敲了.. #include<iostream>//Battle #include<queue> #include<cstdio> #include<cstring> #include<set> #i

BZOJ 1565 植物大战僵尸(最大权闭合图)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1565 题意:植物大战僵尸,一个n*m的格子,每 个格子里有一个植物,每个植物有两个属性:(1)价值:(2)保护集合,也就是这个植物可以保护矩阵中的某些格子.现在你是僵尸,你每次只能从(i,m) 格子进入,从右向左进攻.若一个格子是被保护的那么你是不能进入的.每进入一个格子则吃掉该格子的植物并得到其价值(价值有可能是负的).注意,每次在进 入一行后还可以再退到最右侧然后再换一行吃别的.问

最大权闭合图

定义:在一个图中,我们选取一些点构成集合,记为V,且集合中的出边(即集合中的点的向外连出的弧),所指向的终点(弧头)也在V中,则我们称V为闭合图.最大权闭合图即在所有闭合图中,集合中点的权值之和最大的V,我们称V为最大权闭合图. 做法:首先我们将其转化为一个网络(现在不要问为什么,接下来会证明用网络可以求解).构造一个源点S,汇点T.我们将S与所有权值为正的点连一条容量为其权值的边,将所有权值为负的点与T连一条容量为其权值的绝对值的边,原来的边将其容量定为正无穷. 首先引入结论,最小割所产生的两

HDU 3879:Base Station(最大权闭合图)

http://acm.hdu.edu.cn/showproblem.php?pid=3879 http://www.lydsy.com/JudgeOnline/problem.php?id=1497 题意:给出n个点m条边,其中每个点有一个权值代表修建这个点需要耗费的钱,然后m条边里面,代表如果两个修建好的点相连的话,那么可以得到一点利润.求最大的获利. 思路:和BZOJ 1497是同一道题目.学习最大权闭合图的题目,看了一下不清楚应该怎么建图,然后只好搜一个论文来看看.http://wenku

CSU 1319 CX‘s dreams 最大权闭合图 求最多的正点权个数

题目链接:点击打开链接 思路: 显然就是问最大权闭合图 和 能取最多的正点权个数 1.首先对于正权值的付出,直接取,而对于梦想也忽略正权值的付出,这样就转成一个裸的最大权闭合图了. 2.计算此时的正点权个数:把所有点权*大数C,然后把正点权值+1,跑出来流量就是 flow / C, 最多的正点权个数就是 正点权点集-flow%C. #include <cstdio> #include <cstring> #include <algorithm> #include <

HDU 3879 Base Station 最大权闭合图

题目链接:点击打开链接 题意: 给定n个带权点m条无向带权边 选一个子图,则这个子图的权值为 边权和-点权和 求一个最大的权值 把边也当成点.然后是最大权闭合图 dinic: #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; //点标 从0开始 F.Init(n) n=最大点标+10 const int N =