又是经典模型的好题目

题目描述

人类智慧之神 zhangzj 最近有点胖,所以要减肥,他买了 NN 种减肥药,发现每种减肥药使用了若干种药材,总共正好有 NN 种不同的药材。

经过他的人脑实验,他发现如果他吃下去了 KK(0≤K≤N0≤K≤N)种减肥药,而这 KK 种减肥药使用的药材并集大小也为 KK,这 KK 种才会有效果,否则无效。
第 ii 种减肥药在产生效果的时候会使 zhangzj 的体重增加 PiPi​ 斤,显然 PiPi​ 可以小于 00。

他想知道,一次吃药最好情况下体重变化量是多少,当然可以一种药也不吃,此时体重不变。 由于某些奥妙重重的情况,我们可以让这 NN 种减肥药每一种对应一个其使用的药材,且 NN 种减肥药对应的药材互不相同(即有完美匹配)。

输入格式

第一行一个整数 NN。

接下来 NN 行,每行描述一种减肥药,对于一种减肥药,第一个数读入使用的药材个数 tt,接下来 tt 个整数表示使用的药材编号,一个药材编号在一行只会出现一次。
最后一行 NN 个整数,第 ii 个整数 PiPi​ 表示第 ii 种减肥药产生效果时的体重变化量。

数据范围与提示

对于 30%30% 的数据,N≤20N≤20;
对于另外 10%10% 的数据,Pi<0Pi​<0;
对于 100%100% 的数据,1≤N≤300,∣Pi∣≤10000001≤N≤300,∣Pi​∣≤1000000。


题目分析

这道题最主要的麻烦在于药材和药的数目要相同,这意味着最优情况下,可能需要选取一些收益为负的药来达到合法状态。因此wqs二分药材权值负增量的做法就会挂得很彻底。

再观察题目的特殊性质,发现给出的一张图是恰好两侧点数相同,且具有完美匹配的二分图。这个条件说明在左侧任意选取$k$个点,右侧对应的$k'$必定有$k \le k'$。这是一个相当重要的条件。在这个前提下,我们可以把每种减肥药的权值改为$BASE-P_i$;药材的权值设成$BASE$,再以此做最大权闭合子图。关于这个做法的解释如下所示:

这里是第一个例子,最优方案是三个全选,收益为20。考虑一下“表面权值和最优”(但是不合法)的选前两个的方案,由于我们在右侧每个药材都连出了$BASE$的边,那么当左边选取$k$个、右边选取$k\le k'$个时,进行的操作相当于是选了右侧$k'\times BASE$的割,然而很明显选左侧$k\times BASE-\sum P_i$的割更优。但在最大权闭合子图的方面看,割去$(S,i)$也即意味着点$i$就是不选了,这与前一假设矛盾。因此,右边边权的$BASE$增量能够致使$k\ge k'$,那么综上所述保证了$k=k'$。

以下是第二个例子。在这个例子中,为了保证合法,我们不得不选取增肥100的减肥药。同样是考虑选取前两个元素的方案,会发现仍然由于右侧有三个$BASE,所以导致只有在加入第三个元素时才得到最大值。

  1. #include<bits/stdc++.h>
  2. const int maxn = ;
  3. const int maxm = ;
  4. const int INF = 2e9;
  5. const int base = 5e6;
  6.  
  7. struct Edge
  8. {
  9. int u,v,f,c;
  10. Edge(int a=, int b=, int c=, int d=):u(a),v(b),f(c),c(d) {}
  11. }edges[maxm];
  12. int edgeTot,head[maxn],nxt[maxm],lv[maxn];
  13. int n,ans,tot,S,T;
  14.  
  15. void addedge(int u, int v, int c)
  16. {
  17. edges[edgeTot] = Edge(u, v, , c), nxt[edgeTot] = head[u], head[u] = edgeTot, ++edgeTot;
  18. edges[edgeTot] = Edge(v, u, , ), nxt[edgeTot] = head[v], head[v] = edgeTot, ++edgeTot;
  19. }
  20. bool buildLevel()
  21. {
  22. std::queue<int> q;
  23. memset(lv, , sizeof lv);
  24. q.push(S), lv[S] = ;
  25. for (int tmp; q.size(); )
  26. {
  27. tmp = q.front(), q.pop();
  28. for (int i=head[tmp]; i!=-; i=nxt[i])
  29. {
  30. int v = edges[i].v;
  31. if (!lv[v]&&edges[i].f < edges[i].c){
  32. lv[v] = lv[tmp]+, q.push(v);
  33. if (v==T) return true;
  34. }
  35. }
  36. }
  37. return false;
  38. }
  39. int fndPath(int x, int lim)
  40. {
  41. int sum = ;
  42. if (x==T||!lim) return lim;
  43. for (int i=head[x]; i!=-&&sum < lim; i=nxt[i])
  44. {
  45. int v = edges[i].v, val;
  46. if (lv[v]==lv[x]+&&edges[i].f < edges[i].c){
  47. if ((val = fndPath(v, std::min(lim-sum, edges[i].c-edges[i].f)))){
  48. edges[i].f += val, edges[i^].f -= val, sum += val;
  49. }else lv[v] = -;
  50. }
  51. }
  52. return sum;
  53. }
  54. int dinic()
  55. {
  56. int ret = , val;
  57. while ((buildLevel()))
  58. while ((val = fndPath(S, INF))) ret += val;
  59. return ret;
  60. }
  61. int main()
  62. {
  63. freopen("loj6045.in","r",stdin);
  64. freopen("loj6045.out","w",stdout);
  65. memset(head, -, sizeof head);
  66. scanf("%d",&n), S = , T = *n+;
  67. for (int i=,k; i<=n; i++)
  68. {
  69. scanf("%d",&k);
  70. for (int u; k; --k)
  71. scanf("%d",&u), addedge(i, u+n, INF);
  72. }
  73. for (int i=,s; i<=n; i++)
  74. {
  75. scanf("%d",&s);
  76. addedge(S, i, base-s);
  77. addedge(i+n, T, base);
  78. tot += base-s;
  79. }
  80. printf("%d\n",-tot+dinic());
  81. return ;
  82. }

END

【思维题 最大权闭合子图】loj#6045. 「雅礼集训 2017 Day8」价的更多相关文章

  1. LOJ#6045. 「雅礼集训 2017 Day8」价(最小割)

    题面 传送门 题解 首先先把所有权值取个相反数来求最大收益,因为最小收益很奇怪 然后建图如下:\(S\to\)药,容量\(\inf+p_i\),药\(\to\)药材,容量\(\inf\),药材\(\t ...

  2. 【LYOI 212】「雅礼集训 2017 Day8」价(二分匹配+最大权闭合子图)

    「雅礼集训 2017 Day8」价 内存限制: 512 MiB时间限制: 1000 ms 输入文件: z.in输出文件: z.out   [分析] 蛤?一开始看错题了,但是也没有改,因为不会做. 一开 ...

  3. loj #6046. 「雅礼集训 2017 Day8」爷

    #6046. 「雅礼集训 2017 Day8」爷 题目描述 如果你对山口丁和 G&P 没有兴趣,可以无视题目背景,因为你估计看不懂 …… 在第 63 回战车道全国高中生大赛中,军神西住美穗带领 ...

  4. LOJ#6046. 「雅礼集训 2017 Day8」爷(分块)

    题面 传送门 题解 转化为\(dfs\)序之后就变成一个区间加,区间查询\(k\)小值的问题了,这显然只能分块了 然而我们分块之后需要在块内排序,然后二分\(k\)小值并在块内二分小于它的元素--一个 ...

  5. LOJ#6044. 「雅礼集训 2017 Day8」共(Prufer序列)

    题面 传送门 题解 答案就是\(S(n-k,k)\times {n-1\choose k-1}\) 其中\(S(n,m)\)表示左边\(n\)个点,右边\(m\)个点的完全二分图的生成树个数,它的值为 ...

  6. LOJ #6044 -「雅礼集训 2017 Day8」共(矩阵树定理+手推行列式)

    题面传送门 一道代码让你觉得它是道给初学者做的题,然鹅我竟没想到? 首先考虑做一步转化,我们考虑将整棵树按深度奇偶性转化为一张二分图,即将深度为奇数的点视作二分图的左部,深度为偶数的点视作二分图的右部 ...

  7. [LOJ#6044]. 「雅礼集训 2017 Day8」共[二分图、prufer序列]

    题意 题目链接 分析 钦定 \(k\) 个点作为深度为奇数的点,有 \(\binom{n-1}{k-1}\) 种方案. 将树黑白染色,这张完全二分图的生成树的个数就是我们钦定 \(k\) 个点之后合法 ...

  8. [LOJ 6031]「雅礼集训 2017 Day1」字符串

    [LOJ 6031] 「雅礼集训 2017 Day1」字符串 题意 给定一个长度为 \(n\) 的字符串 \(s\), \(m\) 对 \((l_i,r_i)\), 回答 \(q\) 个询问. 每个询 ...

  9. [LOJ 6030]「雅礼集训 2017 Day1」矩阵

    [LOJ 6030] 「雅礼集训 2017 Day1」矩阵 题意 给定一个 \(n\times n\) 的 01 矩阵, 每次操作可以将一行转置后赋值给某一列, 问最少几次操作能让矩阵全为 1. 无解 ...

随机推荐

  1. 瓷砖覆盖(状压DP)

    题目描述 Description 用1*2的瓷砖去铺N*M的地面,问有多少种铺法 输入描述 Input Description 第一行有两数n,m.表示地面的大小 输出描述 Output Descri ...

  2. spark_learn

    package chapter03 import org.apache.spark.sql.DataFrame import org.apache.spark.sql.hive.HiveContext ...

  3. jdbc操作步骤

    package com.itheima.test; import java.sql.Connection; import java.sql.DriverManager; import java.sql ...

  4. 【Unity3D】简要分析unity3d中剪不断理还乱的yield

    在学习unity3d的时候很容易看到下面这个例子: void Start () { StartCoroutine(Destroy()); } IEnumerator Destroy(){ yield ...

  5. mysql数据库忘记密码时如何修改(二)

    第一步:找到mysql数据库的my.ini配置文件,在[mysqld]下面添加一行代码:skip-grant-tables 第二步:运行services.msc进入服务管理界面,重启mysql服务. ...

  6. css相关知识

    display: block; "块级元素". display: inline; "行内元素". display: none; "在不删除元素的情况下 ...

  7. ElasticSearch多个字段分词查询高亮显示

    ElasticSearch关键字查询,将关键字分词后查询,多个字段,查询出来字段高亮显示. 查询方法如下: public List<NewsInfo> searcher2(String k ...

  8. hibernate课程 初探单表映射1-7 hibernate配置文件新建

    hibernate  配置文件新建 1 右键src==>new==>other==>hibernate configuration File==>next==>next= ...

  9. ubuntu 16.04安装nVidia显卡驱动和cuda/cudnn踩坑过程

    安装深度学习框架需要使用cuda/cudnn(GPU)来加速计算,而安装cuda/cudnn,首先需要安装nvidia的显卡驱动. 我在安装的整个过程中碰到了驱动冲突,循环登录两个问题,以至于最后不得 ...

  10. TCP 协议中的 Window Size与吞吐量

    原地址:http://blog.sina.com.cn/s/blog_c5c2d6690102wpxl.html TCP协议中影响实际业务流量的参数很多,这里主要分析一下窗口的影响. ​TCP窗口目的 ...