题意:

有n条超级大佬贞鱼站成一行,现在你需要使用恰好k辆车把它们全都运走。要求每辆车上的贞鱼在序列中都是连续的。每辆车上的贞鱼会产生互相怨恨的值,设a与b之间的怨恨值为G(a,b),一辆车上的贞鱼的编号从L到R,那么这辆车上的怨恨值为\(\sum_{L<=a,b<=R}G(a,b)\)。注意G(a,b)=G(b,a),一对贞鱼之间的怨恨值只算一次,也就是G(a,b)和G(b,a)只算一次。

1<=n<=4000,1<=k<=min(n,800),0<=G(a,b)<=10

输入格式

第一行两个整数n,k;

接下来是一个n*n的矩阵表示G[][],其中G(i,i)=0.且保证矩阵是对称的。

输出格式

一个整数ans表示怨恨值值之和的最小值。

思路:

DP凸优化的部分

首先,恰好使用k辆车这个限制非常的迷,不禁使我们想到了一个算法:DP凸优化。(要是平时拿给我做,我绝对想不到这玩意)至于什么是DP凸优化,这里就先行跳过讲解了,可以在关于WQS二分算法以及其一个细节证明-by Creeper_LKF里学习一下。

至于这道题,我们发现可以直接二分增加一辆车需要额外增加的代价,就可以限制车的数量。(代价越大,多使用一辆车就可能比较亏,还不如在一辆车里多塞几条贞鱼)。

然后就可以在二分出来的额外代价的意义下跑一个\(O(n^2)\)的dp了,总时间为\(O(n^2\log n)\),要被卡。

单调队列转移的部分

下面考虑如何优化。

首先还是看一下n*n的转移式是怎样的:

\[dp[i]=min(dp[j]+\frac{sum[i][i]-sum[i][j]-sum[j][i]+sum[j][j]}{2})
\]

其中dp[i]表示当前以第i条贞鱼为最后一辆车的最后一条鱼的最小值。然后sum[i][j]为给出的矩阵的前缀和。你会发现后面那一坨就是矩阵中(j+1,j+1)到(i,i)的和,也就是从j+1到i这些贞鱼互相怨恨的值的总和。但是由于题目的要求,还要除以2。

优化的话,仔细观察一波之后可以发现这玩意的转移时具有单调性的(前提是你dp的定义一定要是对的)。

选择单调性的话,是和dp值的增长率的大小有关的。如果不懂的话,可以看一下下面这个图。

首先将式子中的对于i而言的非常数项提出来:-(sum[i][j]+sum[j][i]-sum[j][j]),然后下面的图表现的是括号里面(先令作F(i,j))的几何意义。

j1<j2是显然的.其中橙色部分v表示的就是F(i,j2)随i的变化量,而绿色部分就是F(i,j1)随i的变化量。可见delta(F(i,j2))是大于delta(F(i,j1))的,也就是说dp是中和sum[][]有关的那一坨中中,如果j1<j2,因为对于j1和j2而言,sum[i][i]的增加率肯定是相等的,而F(i,j1)增加的要慢一些,也就是它阻碍sum[i][i]增加的作用要小一些,那么对应的j1的总的增加量就要大一些。

因为我们要求的是最小值,而此时如果dp[j1]还大于dp[j2]的话,在i增大的过程中,j1是绝对不可能成为决策点的。于是就可以把j1舍去了。

这里博主可能说的有些复杂,能理解就好。

这样子就可以利用决策单调性把整体时间复杂度优化到\(O(n\log n\log maxval)\)了。

至于决策单调性的具体过程,大概就是先发现一个决策点对一个连续的区间进行转移,所以可以在队列里面放入一个决策点当前能够更新的左右端点和自己的下标。

首先先默认能够更新到n,然后在之后的插入决策点的过程中,将绝对不可能进行之后的转移的点弹掉,然后在一个完整的区间内部进行二分,将这个区间拆成两个,其中右边的那个就是新插进去的决策点的更新区间啦。(具体参见代码)

坑点

1.转移式一定要记得/2

2.DP凸优化的时候,注意外层二分的写法,最好是自己调一下。

3.在最后算答案加回多算的代价的时候,记得是每辆车多算的代价乘上k,而不是乘上你这个方案的车的数量

4.在DP的转移过程中,除了要将值的大小设为第一比较关键字外,还要将选取车的数量设为第二关键字进行比较。通常是这样的:如果你在外层的二分中是判定当前选取车的数量<= k的时候算答案的话,你在内层就要保证在dp值相同的情况下,尽可能地少使用车;反之亦然(全部反过来就行了)。

代码

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. #include<cstdlib>
  5. #include<cctype>
  6. #define MAXN 4000
  7. #define MAXK 800
  8. #define INF 0x7FFFFFFF
  9. using namespace std;
  10. struct node
  11. {
  12. int l,r,j;
  13. node(){};
  14. node(int _l,int _r,int _j):l(_l),r(_r),j(_j){};
  15. };
  16. int n,dp[MAXN+5],dpnum[MAXN+5],sum[MAXN+5][MAXN+5];
  17. int k;
  18. node que[MAXN+5];
  19. int Get(int j,int i)
  20. {
  21. return dp[j]+(sum[i][i]-sum[i][j]-sum[j][i]+sum[j][j])/2;//记得除以2
  22. }
  23. int Cmp(int v1,int v2,int num1,int num2)//专门用来进行双关键字比较的函数
  24. {
  25. if(v1<v2)
  26. return 1;
  27. else if(v1>v2)
  28. return -1;
  29. else if(num1>num2)
  30. return -1;
  31. else if(num1<num2)
  32. return 1;
  33. return 0;
  34. }
  35. int Check(int x)
  36. {
  37. int s=0,t=0;
  38. que[t++]=node(1,n,0);
  39. dp[0]=0;
  40. for(int i=1;i<=n;i++)
  41. {
  42. while(s<t&&que[s].r<i)
  43. s++;//先判定当前i是否在转移的区间内,否则就舍去,因为以后也用不上了。
  44. int j=que[s].j;
  45. dpnum[i]=dpnum[j]+1;
  46. dp[i]=dp[j]+(sum[i][i]-sum[i][j]-sum[j][i]+sum[j][j])/2+x;
  47. while(s<t&&Cmp(Get(que[t-1].j,que[t-1].l),Get(i,que[t-1].l),dpnum[que[t-1].j],dpnum[i])==-1)
  48. t--;//直接和左端点进行贡献大小的比较,如果更优的话直接就舍去了。
  49. if(s<t)//找一个无法完全取代的决策点
  50. {
  51. int L=que[t-1].l-1,R=que[t-1].r+1;
  52. while(L+1<R)//二分确定这个决策点到底从哪里断开
  53. {
  54. int mid=(L+R)/2;
  55. if(Cmp(Get(i,mid),Get(que[t-1].j,mid),dpnum[i],dpnum[que[t-1].j])==1)
  56. R=mid;
  57. else
  58. L=mid;
  59. }
  60. que[t-1].r=L;
  61. if(R<=n)
  62. que[t++]=node(R,n,i);//不能加入非法区间
  63. }
  64. else
  65. que[t++]=node(i,n,i);//空的就直接加进去
  66. }
  67. return dpnum[n];
  68. }
  69. const int MAXSIZE=1<<15;//网上粘的读入优化,还没搞懂咋回事...恶意卡常,加上去就好了
  70. char buf[MAXSIZE],*p1=buf,*p2=buf;
  71. #define gc p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXSIZE,stdin),p1==p2)?EOF:*p1++
  72. int read()
  73. {
  74. int x=0,f=1;char ch=gc;
  75. while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc;}
  76. while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc;}
  77. return x*f;
  78. }
  79. int main()
  80. {
  81. scanf("%d %d",&n,&k);
  82. for(int i=1;i<=n;i++)
  83. for(int j=1;j<=n;j++)
  84. {
  85. sum[i][j]=read();
  86. // scanf("%d",&sum[i][j]);
  87. sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
  88. }
  89. int L=0,R=INF,mid;
  90. while(L<R)
  91. {
  92. mid=(1LL*L+1LL*R)>>1LL;
  93. int ret=Check(mid);
  94. if(ret<=k)
  95. R=mid;//前者
  96. else
  97. L=mid+1;
  98. }
  99. int ret=Check(R);
  100. int val=dp[n]-R*k;//注意是-R*k而不是-R*ret,因为注重的是答案,而不是方案
  101. printf("%d\n",val);
  102. return 0;
  103. }

【Codeforces 321E / BZOJ 5311】【DP凸优化】【单调队列】贞鱼的更多相关文章

  1. BZOJ.1010.[HNOI2008]玩具装箱toy(DP 斜率优化/单调队列 决策单调性)

    题目链接 斜率优化 不说了 网上很多 这的比较详细->Click Here or Here //1700kb 60ms #include<cstdio> #include<cc ...

  2. 「学习笔记」wqs二分/dp凸优化

    [学习笔记]wqs二分/DP凸优化 从一个经典问题谈起: 有一个长度为 \(n\) 的序列 \(a\),要求找出恰好 \(k\) 个不相交的连续子序列,使得这 \(k\) 个序列的和最大 \(1 \l ...

  3. 【题解】Cats Transport (斜率优化+单调队列)

    [题解]Cats Transport (斜率优化+单调队列) # When Who Problem Lang Verdict Time Memory 55331572 Jun/09/2019 19:1 ...

  4. BZOJ 1499 [NOI2005] 瑰丽华尔兹 | 单调队列优化DP

    BZOJ 1499 瑰丽华尔兹 | 单调队列优化DP 题意 有一块\(n \times m\)的矩形地面,上面有一些障碍(用'#'表示),其余的是空地(用'.'表示).每时每刻,地面都会向某个方向倾斜 ...

  5. bzoj 1499 [NOI2005]瑰丽华尔兹——单调队列优化dp

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1499 简单的单调队列优化dp.(然而当时却WA得不行.今天总算填了坑) 注意滚动数组赋初值应 ...

  6. BZOJ 3126 [USACO2013 Open]Photo (单调队列优化DP)

    洛谷传送门 题目大意:给你一个长度为$n$的序列和$m$个区间,每个区间内有且仅有一个1,其它数必须是0,求整个序列中数字1最多的数量 神题,竟然是$DP$ 定义$f_{i}$表示第i位放一个1时,最 ...

  7. bzoj 3831 Little Bird (单调队列优化dp)

    /*先贴个n*n的*/ #include<iostream> #include<cstdio> #include<cstring> #define maxn 100 ...

  8. bzoj 3126: [Usaco2013 Open]Photo——单调队列优化dp

    Description 给你一个n长度的数轴和m个区间,每个区间里有且仅有一个点,问能有多少个点 Input * Line 1: Two integers N and M. * Lines 2..M+ ...

  9. dp凸优化/wqs二分学习笔记(洛谷4383 [八省联考2018]林克卡特树lct)

    qwq 安利一个凸优化讲的比较好的博客 https://www.cnblogs.com/Gloid/p/9433783.html 但是他的暴力部分略微有点问题 qwq 我还是详细的讲一下这个题+这个知 ...

随机推荐

  1. Git安装与仓库搭建

    yum install git-all cd /srv git init --bare net_server.git git remote - v git remote add server root ...

  2. mac下chrome 长截图(不使用插件)

    1. command + option + i (打开windows下的f12): 2. command + shipt + p ; 3. 输入命令: Capture full size screen ...

  3. python3: 自动化测试框架pytest

    最近在学习web自动化,所以在这里总结一下pytest框架. 其实pytest 和 unittest 都是自动化测试框架,但是pytest更好用一些,有以下几个优点:1)可以根据标签执行用例:2)?? ...

  4. JAVA进阶14

    间歇性混吃等死,持续性踌躇满志系列-------------第14天 1.线程的加入 package code0328; import javax.swing.*; import java.awt.* ...

  5. C# 创建Web项目时 可以选择的类型在不同VS版本下的对比

    上面这个界面应该是 vs2010的 一. VS2012 .VS2013 其实每个模板的意思,在右边已经显示出来了.Empty,就是一个空的模板,创建后里面除了一个web.config外什么都没有:We ...

  6. Running Tensorflow on AMD GPU

    keras+tensorflow: based on AMD GPU https://rustyonrampage.github.io/deep-learning/2018/10/18/tensorf ...

  7. c# Lamdba及DataTable AsEnumerable()的使用

    Lamdba是延迟执行的,实际上什么都没有发生,当真正使用对象的时候(例如调用:First, Single, ToList-.的时候)才执行. 1.Where var var_dtTable = dt ...

  8. go实现选择排序

    package main import "fmt" func SelectSort(data []int) { length := len(data) for i := 0; i ...

  9. Sql语句基础练习(一)

    1.求1号课成绩大于80分的学生的学号及成绩,并按成绩由高到低列出.(表名:成绩表.字段名:课号,学号,成绩.) SELECT 学号,成绩 FROM 成绩表 WHERE 课号=1 AND 成绩> ...

  10. springboot 多模块打war 部署

    先展示一下自己项目结构 一共有5个模块 依赖关系:下面的模块依赖上面所有的模块 其中 rongke-web是我要部署的模块 最终要打war进行部署,其他模块均打jar 被rongke-web引用. 开 ...