BZOJ 4873: [Shoi2017]寿司餐厅 (最大权闭合子图)
来源:互联网 发布:上海美国学校 知乎 编辑:程序博客网 时间:2024/04/29 00:09
Solution
这题也如同day1第三题一样,是一道看懂题目并转化后发现是很水的题。。(这个前提非常重要)
考试时我想day1没考网络流,day2肯定有吧。没想到被我说中了。前两题耗了很久,第三题看题目那么长,想了想发现正解不是dp就是网络流(嘴巴AC),甚至想到跑最小割,但死活卡在构图,又去想dp,无果,然后就被迫弃了。。千古蒟蒻。。
讲讲正解。
考虑最大权闭合子图,对于长度大于1的区间,区间[i,j]要取的话,[i+1,j]和[i,j-1]都必须取(因为要取连续的一段),而题目中又是分次数取,但又有一些限制,记花费也有限制。这又是网络流的套路。
于是我们按最大权闭合子图构图。[i,j]向[i+1,j],[i,j-1]连边,容量为INF。
然后取费用就构出a[i](最多1000个),容量保存费用,连t。s向正权点连边,负权点向t连边。
对于长度为1的区间[i,i],无后继,将其价值减去a[i](将花费分解,注意题目中的c是没用的!),并且前置条件(连向的点)是权值为m*a[i]*a[i]的点。
然后跑最小割就行了。
这里复习一下最大权闭合子图的构图和作法。
最大权闭合图
定义一个有向图(有点权)的闭合图(closure)是该有向图的一个点集,且该点集的所有出边都还指向该点集。即闭合图内的任意点的任意后继也一定在闭合图中。
按照上面的定义,闭合图是允许超过一个连通块的
给每个点 分配一个点权 (任意实数,可正可负)。
最大权闭合图是指一个点权之和最大的闭合图算法
1、在原图上增加源 s和汇t。
2、将原图每条边(u,v)替换为容量为+oo 的有向边 (u, v)。
3、由s到原图每个正权点 连有向边,容量为该点点权
4、由每个负权点向t连容量为-点权的有向边
最大权为。。。
感性认识
先全部正权都选了。
割s的出边即不选这个点。
割t的入边即选了这个点
若存在s到t路径则不满足这条路径所代表的依赖关系。
故答案为
正权和减最小割
复习到这里,按题目要求构完图后剩下的就是Dinic跑最小割了。。
Code
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>#include <queue>#include <cstdlib>#define INF 0x7fffffff#define N 111#define An 1000#define maxn N * N + Anusing namespace std;int n, m, s, t, sum;int a[N], d[N][N], num[N][N];int level[maxn], iter[maxn], head_p[maxn], cur = -1;struct Tadj{int next, obj, cap;} Edg[N*N*6+An*2];queue <int> q;void Insert(int a, int b, int c){ cur ++; Edg[cur].next = head_p[a]; Edg[cur].obj = b; Edg[cur].cap = c; head_p[a] = cur;}bool bfs(){ q.push(s); memset(level, -1, sizeof(level)); level[s] = 0; while(!q.empty()){ int now = q.front(); q.pop(); for(int i = head_p[now]; ~ i; i = Edg[i].next){ int v = Edg[i].obj, c = Edg[i].cap; if(c && level[v] == -1){ level[v] = level[now] + 1; q.push(v); } } } return ~level[t];}int Dinic(int now, int f){ if(now == t) return f; for(int &i = iter[now]; ~ i; i = Edg[i].next){ int v = Edg[i].obj, c = Edg[i].cap; if(c && level[now] < level[v]){ int d = Dinic(v, min(f, c)); if(d){ Edg[i].cap -= d; Edg[i^1].cap += d; return d; } } } return 0;}int Min_cut(){ int flow = 0, f; for(;;){ if(!bfs()) return flow; for(int i = s; i <= t; i++) iter[i] = head_p[i]; while((f = Dinic(s, INF)) > 0) flow += f; }}int main(){ freopen("sushi.in", "r", stdin); freopen("sushi.out", "w", stdout); scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); s = 1; t = s + An + n * n + 1; for(int i = s; i <= t; i++) head_p[i] = -1; for(int i = 1; i <= An; i++){ Insert(s+i, t, m * i * i); Insert(t, s+i, 0); } int cnt = 1; for(int i = 1; i <= n; i++) for(int j = i; j <= n; j++){ scanf("%d", &d[i][j]); num[i][j] = s + An + cnt ++; } for(int i = 1; i <= n; i++) for(int j = i; j <= n; j++){ if(i == j){ d[i][j] -= a[i]; Insert(num[i][j], s+a[i], INF); Insert(s+a[i], num[i][j], 0); } else{ Insert(num[i][j], num[i+1][j], INF); Insert(num[i+1][j], num[i][j], 0); Insert(num[i][j], num[i][j-1], INF); Insert(num[i][j-1], num[i][j], 0); } if(d[i][j] > 0){ sum += d[i][j]; Insert(s, num[i][j], d[i][j]); Insert(num[i][j], s, 0); } else{ Insert(num[i][j], t, -d[i][j]); Insert(t, num[i][j], 0); } } printf("%d\n", sum - Min_cut()); return 0;}
Victory won’t come to you unless you go to it.
- BZOJ 4873: [Shoi2017]寿司餐厅 (最大权闭合子图)
- [最大权闭合子图] BZOJ 4873 [Shoi2017]寿司餐厅
- bzoj 4873: [Shoi2017]寿司餐厅 最大权闭合子图
- [最大权闭合子图] BZOJ4873: [Shoi2017] 寿司餐厅
- [最大权闭合子图][最小割] BZOJ 4873:寿司餐厅
- bzoj 4873 [Shoi2017]寿司餐厅
- 4873: [Shoi2017]寿司餐厅
- BZOJ 3438 最大权闭合子图
- BZOJ 1497 最大权闭合子图
- BZOJ4873 [Shoi2017]寿司餐厅
- BZOJ4873: [Shoi2017]寿司餐厅
- bzoj4873: [Shoi2017]寿司餐厅
- BZOJ4873: [Shoi2017]寿司餐厅
- BZOJ 3996: [TJOI2015]线性代数(最大权闭合子图)
- bzoj 1497(最小割,最大权闭合子图)
- bzoj 1565 植物大战僵尸【最大权闭合子图】
- 【BZOJ 1497】 [NOI2006]最大获利 最大权闭合子图
- BZOJ 1565 (NOI 2009) 最大权闭合子图
- 内存和硬盘上的数据存储方式
- 成功的背后
- 2000年分区联赛普级组之三 乘积最大
- 这一波知识付费即将迎来拐点?
- shiro流程
- BZOJ 4873: [Shoi2017]寿司餐厅 (最大权闭合子图)
- 面试题:Java中对象序列化接口(Serializable)的意义
- iOS 即时通讯常用一些概念
- hdu 5542 The Battle of Chibi(DP+树状数组+离散化)
- Android CoordinatorLayout初次使用
- Unity3D手游开发日记(1)
- Unity 5.6 beta版本新特性
- Nutch2.3(一)
- STL中的vector源码剖析