@description@

「人生就像一盒巧克力,你永远不知道吃到的下一块是什么味道。」

明明收到了一大块巧克力,里面有若干小块,排成 n 行 m 列。每一小块都有自己特别的图案 \(c_{i, j}\),它们有的是海星,有的是贝壳,有的是海螺……其中还有一些因为挤压,已经分辨不出是什么图案了。明明给每一小块巧克力标上了一个美味值 \(a_{i, j}\)(\(0 \leq a_{i, j} \leq 1\)),这个值越大,表示这一小块巧克力越美味。

​正当明明咽了咽口水,准备享用美味时,舟舟神奇地出现了。看到舟舟恳求的目光,明明决定从中选出一些小块与舟舟一同分享。

​舟舟希望这些被选出的巧克力是连通的(两块巧克力连通当且仅当他们有公共边),而且这些巧克力要包含至少 k(\(1 \leq k \leq 5\))种。而那些被挤压过的巧克力则是不能被选中的。

明明想满足舟舟的愿望,但他又有点「抠」,想将美味尽可能多地留给自己。所以明明希望选出的巧克力块数能够尽可能地少。如果在选出的块数最少的前提下,美味值的中位数(我们定义 n 个数的中位数为第 \(\lfloor \frac{n + 1}{2} \rfloor\) 小的数)能够达到最小就更好了。

你能帮帮明明吗?

原题传送门。

@solution@

当 c 比较小时,求块数最少的包含 k 种颜色的连通块,其实就是一个斯坦纳树问题。

可以用二分的方法求中位数 x:将 <= x 记作 -1,> x 记作 1,则中位数是满足权值和 <= 0 的最小 x。因此做斯坦纳树的时候再加一维即可。

然而这个算法只适用于 c 较小的情况。但是注意到 k 依然非常小,也许我们依然可以沿用二分 + 斯坦纳树这一思路。

我们把所有颜色随机分为 k 类,再对这 k 个类搞斯坦纳树;可以发现这样的随机算法做一次成功的概率是 \(\frac{k!}{k^k}\)。

因为 k 非常小,做个 150 次是没有问题的,失败概率大概 0.003(当然你常数小的话可以多做几次)。

@accepted code@

  1. #include <queue>
  2. #include <cstdio>
  3. #include <algorithm>
  4. using namespace std;
  5. const int MAXN = 233;
  6. const int MAX = int(1E6);
  7. const int dx[] = {0, 0, 1, -1};
  8. const int dy[] = {1, -1, 0, 0};
  9. struct node{
  10. int x, y; node() {}
  11. node(int _x, int _y) : x(_x), y(_y) {}
  12. friend node operator + (node a, node b) {
  13. return node(a.x + b.x, a.y + b.y);
  14. }
  15. friend bool operator < (node a, node b) {
  16. return (a.x == b.x ? a.y < b.y : a.x < b.x);
  17. }
  18. }f[MAXN + 5][MAXN + 5][1 << 5];
  19. int n, m, k, t;
  20. int b[MAXN + 5][MAXN + 5], d[MAXN + 5][MAXN + 5];
  21. bool inq[MAXN + 5][MAXN + 5];
  22. void run(int s) {
  23. queue<node>que;
  24. for(int i=1;i<=n;i++)
  25. for(int j=1;j<=m;j++)
  26. if( b[i][j] != -1 ) que.push(node(i, j)), inq[i][j] = true;
  27. while( !que.empty() ) {
  28. node p = que.front(); que.pop(); inq[p.x][p.y] = false;
  29. for(int i=0;i<4;i++) {
  30. int x0 = p.x + dx[i], y0 = p.y + dy[i];
  31. if( x0 < 1 || y0 < 1 || x0 > n || y0 > m || b[x0][y0] == -1 ) continue;
  32. if( f[p.x][p.y][s] + node(1, d[p.x][p.y]) < f[x0][y0][s] ) {
  33. f[x0][y0][s] = f[p.x][p.y][s] + node(1, d[p.x][p.y]);
  34. if( !inq[x0][y0] ) que.push(node(x0, y0)), inq[x0][y0] = true;
  35. }
  36. }
  37. }
  38. }
  39. node check() {
  40. for(int i=1;i<=n;i++)
  41. for(int j=1;j<=m;j++) {
  42. if( b[i][j] == -1 ) continue;
  43. for(int s=0;s<t;s++)
  44. f[i][j][s] = node(MAX, 0);
  45. f[i][j][1 << b[i][j]] = node(0, 0);
  46. }
  47. for(int s=1;s<t;s++) {
  48. for(int i=1;i<=n;i++)
  49. for(int j=1;j<=m;j++) {
  50. if( b[i][j] == -1 ) continue;
  51. int s2 = s & (s - 1);
  52. while( s2 ) {
  53. f[i][j][s] = min(f[i][j][s], f[i][j][s2] + f[i][j][s^s2]);
  54. s2 = s & (s2 - 1);
  55. }
  56. }
  57. run(s);
  58. }
  59. node p = node(MAX, MAX);
  60. for(int i=1;i<=n;i++)
  61. for(int j=1;j<=m;j++) {
  62. if( b[i][j] == -1 ) continue;
  63. p = min(p, f[i][j][t - 1] + node(1, d[i][j]));
  64. }
  65. return p;
  66. }
  67. int a[MAXN + 5][MAXN + 5];
  68. node get() {
  69. if( check().x == MAX ) return node(MAX, MAX);
  70. int l = 0, r = MAX;
  71. while( l < r ) {
  72. int mid = (l + r) >> 1;
  73. for(int i=1;i<=n;i++)
  74. for(int j=1;j<=m;j++)
  75. d[i][j] = (a[i][j] <= mid ? -1 : 1);
  76. if( check().y <= 0 ) r = mid;
  77. else l = mid + 1;
  78. }
  79. return node(check().x, l);
  80. }
  81. node ans;
  82. void update(node p) {
  83. if( ans.x == -1 ) {
  84. if( p.x != MAX ) ans = p;
  85. }
  86. else ans = min(ans, p);
  87. }
  88. int c[MAXN + 5][MAXN + 5], num[MAXN + 5];
  89. void solve() {
  90. int p; scanf("%d%d%d", &n, &m, &k), p = n*m, t = (1 << k);
  91. for(int i=1;i<=n;i++)
  92. for(int j=1;j<=m;j++)
  93. scanf("%d", &c[i][j]);
  94. for(int i=1;i<=n;i++)
  95. for(int j=1;j<=m;j++)
  96. scanf("%d", &a[i][j]);
  97. ans.x = ans.y = -1;
  98. for(int i=1;i<=150;i++) {
  99. for(int j=1;j<=p;j++) num[j] = rand() % k;
  100. for(int x=1;x<=n;x++)
  101. for(int y=1;y<=m;y++)
  102. b[x][y] = (c[x][y] == -1 ? -1 : num[c[x][y]]);
  103. update(get());
  104. }
  105. printf("%d %d\n", ans.x, ans.y);
  106. }
  107. int main() {
  108. srand(20041112);
  109. int T; scanf("%d", &T);
  110. while( T-- ) solve();
  111. }

@details@

二分之前先判是否有合法解会更快一点。

@loj - 2977@ 「THUSCH 2017」巧克力的更多相关文章

  1. LOJ#2977. 「THUSCH 2017」巧克力(斯坦纳树+随机化)

    题目 题目 做法 考虑部分数据(颜色较少)的: 二分中位数\(mid\),将\(v[i]=1000+(v[i]>mid)\) 具体二分操作:然后求出包含\(K\)种颜色的联通快最小的权值和,判断 ...

  2. LOJ 2997 「THUSCH 2017」巧克力——思路+随机化+斯坦纳树

    题目:https://loj.ac/problem/2977 想到斯坦纳树.但以为只能做 “包含一些点” 而不是 “包含一些颜色” .而且不太会处理中位数. 其实 “包含一些颜色” 用斯坦纳树做也和普 ...

  3. LOJ #2978「THUSCH 2017」杜老师

    听说LOJ传了THUSC题赶紧上去看一波 随便点了一题都不会做想了好久才会写暴力爆了一发过了... LOJ #2978 题意 $ T$次询问,每次询问$ L,R$,问有多少种选取区间中数的方案使得选出 ...

  4. LOJ 2980 「THUSCH 2017」大魔法师——线段树

    题目:https://loj.ac/problem/2980 线段树维护矩阵. 然后是 30 分.似乎是被卡常了?…… #include<cstdio> #include<cstri ...

  5. LOJ 2979 「THUSCH 2017」换桌——多路增广费用流

    题目:https://loj.ac/problem/2979 原来的思路: 优化连边.一看就是同一个桌子相邻座位之间连边.相邻桌子对应座位之间连边. 每个座位向它所属的桌子连边.然后每个人建一个点,向 ...

  6. LOJ 2978 「THUSCH 2017」杜老师——bitset+线性基+结论

    题目:https://loj.ac/problem/2978 题解:https://www.cnblogs.com/Paul-Guderian/p/10248782.html 第 i 个数的 bits ...

  7. loj#2978. 「THUSCH 2017」杜老师(乱搞)

    题面 传送门 题解 感谢yx巨巨 如果一个数是完全平方数,那么它的所有质因子个数都是偶数 我们把每一个数分别维护它的每一个质因子的奇偶性,那么就是要我们选出若干个数使得所有质因子的个数为偶数.如果用线 ...

  8. 「THUSCH 2017」大魔法师 解题报告

    「THUSCH 2017」大魔法师 狗体面太长,帖链接了 思路,维护一个\(1\times 4\)的答案向量表示\(A,B,C,len\),最后一个表示线段树上区间长度,然后每次的操作都有一个转移矩阵 ...

  9. LOJ 2288「THUWC 2017」大葱的神力

    LOJ 2288「THUWC 2017」大葱的神力 Link Solution 比较水的提交答案题了吧 第一个点爆搜 第二个点爆搜+剪枝,我的剪枝就是先算出 \(mx[i]\) 表示选取第 \(i \ ...

随机推荐

  1. JavaDoc文件如何生成

    目录 如何使用 1.通过命令行生成JavaDoc文档 2.IDEA如何配置后生成javadoc文档 javadoc命令是用来生成自己的API文档的 参考信息: @author 作者名 @version ...

  2. 基于SpringCloud分布式架构

    基于SpringCloud分布式架构 为什么要使用分布式架构 Spring Cloud 专注于提供良好的开箱即用经验的典型用例和可扩展性机制覆盖 分布式/版本化配置 服务注册和发现 路由 Servic ...

  3. 好用的python性能测试神器–Locust

    原文链接:https://mp.weixin.qq.com/s/9PxSPuHmucSLi_welq6uNQ 现在性能测试工具太多,根据业务不同使用,比如说我们熟悉的loadrunner.jmeter ...

  4. Nginx 配置文件语法

    一.语法规则: location [=|~|~*|^~] /uri/ { … } = 开头表示精确匹配 ^~ 开头表示uri以某个常规字符串开头,理解为匹配 url路径即可.nginx不对url做编码 ...

  5. C#用Linq对列表/集合进查询

    namespace ---> System.Linq; 使用&&进行多条件查询 也可以直接使用Lambda表达式+扩展方法的写法:

  6. eatwhatApp开发实战(三)

    在实战二中我们在eatwhatApp上增加了“添加店铺的功能”.接下来,我们来将添加的店铺显示出来,这里我们用到控件--ListView. 先上演示图: 首先,我们先设置布局: <Relativ ...

  7. [256个管理学理论]003.鳄鱼法则(Alligator Principle)

    鳄鱼法则(Alligator Principle) 来自于大洋彼岸的让你看不懂的解释: 这是经济学交易技术法则之一,也叫“鳄鱼效应”,它的意思是:假定一只鳄鱼咬住你的脚,如果你用手去试图挣脱你的脚,鳄 ...

  8. 【JVM】垃圾回收的四大算法

    GC垃圾回收 JVM大部分时候回收的都是新生代(伊甸区+幸存0区+幸存1区).按照回收的区域可以分成两种类型:Minor GC和Full GC(MajorGC). Minor GC:只针对新生代区域的 ...

  9. 07 . Nginx常用模块及案例

    访问控制 用户访问控制 ngx_http_auth_basic_module 有时我们会有这么一种需求,就是你的网站并不想提供一个公共的访问或者某些页面不希望公开,我们希望的是某些特定的客户端可以访问 ...

  10. HTML元素跟随鼠标一起移动,网页中回到顶部按钮的实现

    对象跟随鼠标: 1.对象css设置绝对定位position: absolute; 2.获取鼠标坐标: 3.通过鼠标坐标计算出对象坐标位置,并设置为css定位的位置: document.onmousem ...