此篇收集各类DP题。

《1D1D动态规划优化初步》的3个模型

1. f[x] = min(f[i]+w[i, x]), i < x且w[i, x]满足单调性(即w[i, j]+w[i+1, j+1] <= w[i, j+1]+w[i+1, j],下四边形同)

2. f[x] = min(g[i])+w[x], b[x] <= i < x, b[x]单调递增

3. f[i]=max{x[j]*a[i]+y[j]*b[i]} = b[i]*max{a[i]/b[i]*x[j]+y[j]},变成了斜率优化

一. 四边形不等式

eg. Hiho1621:超市规划

dp[i, j] = min{ dp[k, j-1] + w[k+1, i] }, 把长度为i的序列切分成j段,可糙猛快分治

dp[i, j] = min{ dp[i, k-1] + dp[k, j] + w[i, j] }, 合并[i, j]区间。注意决策点应该在哪些范围之内单调,比如对于区间dp的方程来说,决策点的单调范围就应该是行号小于等于列号的那一部分。

以上形式通常情况下w会满足四边形不等式,会使得决策opt[i, j] 与 opt[i, j-1], opt[i-1, j]满足单调性。

 #include<bits/stdc++.h>
using namespace std;
const int N = ;
const double inf = 1e17;
double dp[N][N], c[N][N];
double a[N], sum1[N], sum2[N], opt[N][N];
int main()
{
int n, m;
scanf("%d%d",&n,&m);
for(int i = ; i <= n; i++)
scanf("%lf", a+i);
sort(a+, a+n+);
for(int i = ; i <= n; i++) {
sum1[i] = sum1[i-]+a[i];
sum2[i] = sum2[i-]+a[i]*a[i];
}
for(int i = ; i <= n; i++)
for(int j = i+; j <= n; j++){
double mid = (sum1[j]-sum1[i-])/(j-i+);
c[i][j] = (sum2[j]-sum2[i-])+(j-i+)*mid*mid-*mid*(sum1[j]-sum1[i-]);
}
for(int i = ; i <= m; i++)
for(int j = ; j <= n; j++)
dp[i][j] = inf;
for(int i = ; i <= n; i++) dp[][i] = c[][i];
for(int i = ; i <= m; i++){
opt[i][n+] = n;
for(int j = n; j >= ; j--){
for(int k = opt[i-][j]; k <= opt[i][j+]; k++)
if(dp[i][j] > dp[i-][k]+c[k+][j]){
opt[i][j] = k;
dp[i][j] = dp[i-][k]+c[k+][j];
}
}
}
printf("%.3lf\n", dp[m][n]);
return ;
}

决策单调且不要求在线的糙快猛优化方法(for内写成i <= dr && i < m会有bug,m = 1的时候,循环无法进入,导致f[1]值无穷大)

 //个人感觉适用于状态数为二维的dp, 下一行(列)由前一行(列)推出, 且满足决策单调性
void solve(int l, int r, int dl, int dr) {
if(l > r) return ;
int m = (l+r) >> , dm;
for(int i = dl; i <= dr; i++) {
if(i更新f[m]更优) {
dm = i;
更新f[m];
}
}
solve(l, m-, dl, dm), solve(m+, r, dm, dr);
}

基于决策单调的糙猛快分治

 #include<bits/stdc++.h>
using namespace std;
const int N = ;
const double inf = 1e17;
double dp[N][N], c[N][N];
double a[N], sum1[N], sum2[N], opt[N][N];
void solve(double *f1, double *f2, int l, int r, int dl, int dr) {
if(l > r) return ;
int m = (l+r) >> , dm;
for(int i = dl; i <= dr; i++) {
if(f1[i]+c[i+][m] < f2[m])
f2[m] = f1[i]+c[i+][m], dm = i;
}
solve(f1, f2, l, m-, dl, dm);
solve(f1, f2, m+, r, dm, dr);
}
int main() {
int n, m;
scanf("%d%d",&n,&m);
for(int i = ; i <= n; i++)
scanf("%lf", a+i);
sort(a+, a+n+);
for(int i = ; i <= n; i++) {
sum1[i] = sum1[i-]+a[i];
sum2[i] = sum2[i-]+a[i]*a[i];
}
for(int i = ; i <= n; i++)
for(int j = i+; j <= n; j++){
double mid = (sum1[j]-sum1[i-])/(j-i+);
c[i][j] = (sum2[j]-sum2[i-])+(j-i+)*mid*mid-*mid*(sum1[j]-sum1[i-]);
} for(int i = ; i <= m; i++)
for(int j = ; j <= n; j++)
dp[i][j] = inf;
for(int i = ; i <= n; i++) dp[][i] = c[][i];
for(int i = ; i <= m; i++)
solve(dp[i-], dp[i], , n, , n);
printf("%.3f\n", dp[m][n]);
return ;
}

eg. hiho1933

四边形不等式优化

 #include <bits/stdc++.h>
using namespace std;
const int N = 1e4+; long long a[N], sum[N], dp[N][];
int opt[N][]; long long get_sum(int l, int r) {
int m = (l+r) >> ;
long long ans1 = a[m]*(m-l)-(sum[m-]-sum[l-]);
long long ans2 = (sum[r]-sum[m])-a[m]*(r-m);
return ans1+ans2;
} int main() {
int n, k; cin >> n >> k;
for(int i = , x, y; i <= n; i++) {
cin >> x >> y;
a[i] = y;
}
sort(a+, a+n+);
for(int i = ; i <= n; i++) sum[i] = sum[i-]+a[i]; memset(dp, 0x3f, sizeof(dp));
for(int i = ; i <= k; i++) dp[][i] = ; //这里i <= k写成了i <= n, 一直WA
for(int i = ; i <= n; i++) {
opt[i][k+] = n;
for(int j = k; j; j--) {
for(int kk = opt[i-][j]; kk <= opt[i][j+]; kk++) {
long long tmp = dp[kk][j-]+get_sum(kk+, i);
if(tmp < dp[i][j])
dp[i][j] = tmp, opt[i][j] = kk;
}
}
}
cout << dp[n][k] << endl;
return ;
}

基于决策单调的糙猛快分治

 #include <bits/stdc++.h>
using namespace std;
const int N = 1e4+; long long a[N], sum[N], dp[][N]; long long get_sum(int l, int r) {
if(l >= r) return ;
int m = (l+r) >> ;
long long ans1 = a[m]*(m-l)-(sum[m-]-sum[l-]);
long long ans2 = (sum[r]-sum[m])-a[m]*(r-m);
return ans1+ans2;
} void solve(long long *f1, long long *f2, int l, int r, int dl, int dr) {
if(l > r) return ;
int m = (l+r) >> , dm;
for(int i = dl; i <= dr; i++) {
long long tmp = f1[i]+get_sum(i+, m);
if(tmp < f2[m]) {
f2[m] = tmp;
dm = i;
}
}
solve(f1, f2, l, m-, dl, dm);
solve(f1, f2, m+, r, dm, dr);
} int main() {
int n, k; cin >> n >> k;
for(int i = , x, y; i <= n; i++) {
cin >> x >> y;
a[i] = y;
}
sort(a+, a+n+);
for(int i = ; i <= n; i++) sum[i] = sum[i-]+a[i]; for(int i = ; i <= n; i++)
dp[][i] = get_sum(, i);
for(int i = ; i <= k; i++) {
bool now = (i&);
memset(dp[now], 0x3f, sizeof(dp[now]));
solve(dp[!now], dp[now], , n, , n);
}
cout << dp[k&][n] << endl;
return ;
}

二. 斜率优化

eg. HDU3507

f[i] = min(f[j]+(s[i]-s[j])²)+M

= min(f[j]+s[j]²-2s[i]s[j])+M+s[i]²

= min(kx+b)+M+s[i]², 其中k = -2s[j], b = f[j]+s[j]², x = s[i]

注:你也可看作k = 2s[i], x = s[j], y = f[j]+s[j]², f[i] = min(y-kx), k是定值,

则求k为定值的直线y = kx+b的最小截距。脑补画面,将斜率为k的直线移到最下端....

个人认为将整个min里的值当作y,当前已知值作为自变量比较好...

其它题目的变形:f[i]=max{x[j]*a[i]+y[j]*b[i]} = b[i]*max{a[i]/b[i]*x[j]+y[j]}

因为求min,所以维护倒U形(上凸)

因为k递减,x递增,所以用单调队列维护即可,队列内直线按k递减排序,维护倒U形。

代码区===待填===

ps.

如果x无序,则每次二分查找;

如果k无序,可以用set维护,插入的时候erase掉两侧不符合的直线;

如果x, k都无序,可以用set按k值排序维护直线,查找的时候再二分k,lowerbound计算在哪条斜率的直线上(判断x值是否在该直线的两端点内,不在则继续二分)

三、WQS二分

作用就是题目给了一个选物品的限制条件,要求刚好选m个,让你最大化(最小化)权值,然后其特点就是当选的物品越多的时候权值越大(越小)。

http://codeforces.com/blog/entry/49691

https://www.cnblogs.com/HocRiser/p/9834069.html

https://www.cnblogs.com/flashhu/p/9480669.html

https://www.cnblogs.com/CreeperLKF/p/9045491.html

=================================================================================================

区间dp

括号dp:

hihocoder1891:有多少种填充*的方案使得括号匹配?

 #include <bits/stdc++.h>

 #define ll long long
#define ull unsigned long long
#define st first
#define nd second
#define pii pair<int, int>
#define pil pair<int, ll>
#define pli pair<ll, int>
#define pll pair<ll, ll>
#define tiii tuple<int, int, int>
#define pw(x) ((1LL)<<(x))
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
#define sqr(x) ((x)*(x))
#define SIZE(A) ((int)(A.size()))
#define LENGTH(A) ((int)(A.length()))
#define FIN freopen("A.in","r",stdin);
#define FOUT freopen("A.out","w",stdout);
using namespace std;
/***********/ template<typename T>
bool scan (T &ret) {
char c;
int sgn;
if (c = getchar(), c == EOF) return ; //EOF
while (c != '-' && (c < '' || c > '') ) c = getchar();
sgn = (c == '-') ? - : ;
ret = (c == '-') ? : (c - '');
while (c = getchar(), c >= '' && c <= '') ret = ret * + (c - '');
ret *= sgn;
return ;
}
template<typename N,typename PN>inline N flo(N a,PN b){return a>=?a/b:-((-a-)/b)-;}
template<typename N,typename PN>inline N cei(N a,PN b){return a>?(a-)/b+:-(-a/b);}
template<typename T>inline int sgn(T a) {return a>?:(a<?-:);}
template<class T> int countbit(const T &n) { return (n==)?:(+countbit(n&(n-))); }
template <class T1, class T2>
bool gmax(T1 &a, const T2 &b) { return a < b? a = b, :;}
template <class T1, class T2>
bool gmin(T1 &a, const T2 &b) { return a > b? a = b, :;}
template <class T> inline T lowbit(T x) {return x&(-x);} template<class T1, class T2>
ostream& operator <<(ostream &out, pair<T1, T2> p) {
return out << "(" << p.st << ", " << p.nd << ")";
}
template<class A, class B, class C>
ostream& operator <<(ostream &out, tuple<A, B, C> t) {
return out << "(" << get<>(t) << ", " << get<>(t) << ", " << get<>(t) << ")";
}
template<class T>
ostream& operator <<(ostream &out, vector<T> vec) {
out << "("; for(auto &x: vec) out << x << ", "; return out << ")";
}
void testTle(int &a){
while() a = a*(ll)a%;
}
const ll inf = 0x3f3f3f3f;
const ll INF = 1e17;
const int mod = ;;
const double eps = 1e-;
const int N = +;
const double pi = acos(-1.0); /***********/ char s[N];
long long dp[][N];
void add(long long &x, long long y) {
x += y;
if(x >= mod) x %= mod;
}
int main() {
scanf("%s", s);
int tag = , modify = , base = , l = ;
dp[][] = ;
for(int i = ; s[i]; i++) {
if(s[i] == '(')
base++;
else if(s[i] == ')') {
if(base) base--;
else {
l--;
if(l == ) { puts(""); return ; }
modify++;
}
}
else {
if(modify) {
tag = !tag;
for(int j = ; j < l; j++)
dp[tag][j] = dp[!tag][j+modify];
modify = ;
}
tag = !tag;
if(base) {
memset(dp[tag], , sizeof(long long)*(l+));
for(int j = ; j < l; j++) {
add(dp[tag][j], dp[!tag][j]);
add(dp[tag][j+], dp[!tag][j]);
}
base--, l += ;
}
else {
memset(dp[tag], , sizeof(long long)*(l+));
for(int j = ; j < l; j++) {
if(j) add(dp[tag][j-], dp[!tag][j]);
add(dp[tag][j+], dp[!tag][j]);
}
l++;
}
}
}
if(modify) {
tag = !tag;
for(int j = ; j < l; j++)
dp[tag][j] = dp[!tag][j+modify];
modify = ;
}
printf("%lld\n", base? : dp[tag][]);
return ;
}

DPHARD的更多相关文章

随机推荐

  1. c语言数字图像处理(十):阈值处理

    定义 全局阈值处理 假设某一副灰度图有如下的直方图,该图像由暗色背景下的较亮物体组成,从背景中提取这一物体时,将阈值T作为分割点,分割后的图像g(x, y)由下述公式给出,称为全局阈值处理 多阈值处理 ...

  2. 在Visual Studio中使用.lib和.dll的环境搭建

    1 静态库和动态链接库的区别 动态链接库是在运行的时候被调用的,静态库在链接的时候被链接到最终生成的应用程序(.exe)中 静态库需要用到的文件 (.lib .h) 头文件(.h)提供接口,库文件(. ...

  3. VS Code配置初探

    之前一直在用 Webstorm,看现在 VS Code 热度那么高,想着尝试一下. 熟悉编辑器的快捷键 VS Code 快捷键一览 安装使用到的插件 Chinese(修改你的编辑器语言,默认英文) E ...

  4. gitlab+jenkins持续集成(三)

    构建: 需要将jenkins服务器上  jenkins用户的公钥发送给  目标服务器的gs用户,使得在jenkins上能用gs免密登录目标服务器 复制密钥到目标机器上(需要登录到的机器) ssh-co ...

  5. Memcached服务器上实现多个实例(约约问题排查)

    约约测试服上出行一个问题,司机收车失败. (1)经查看代码是null指针异常. 针对,之前,同套代码发布到华威测试服,未出现该问题,遂认定不是代码问题. (2)打印异常信息,获取null值异常的收车司 ...

  6. salt-api https连接问题

    在非salt-api的主机上测试api连通性,测试代码如下: #!/usr/bin/env python import pycurl import StringIO import ssl ssl._c ...

  7. Linux 文件系统 -- 文件权限简介

    一.文件权限 使用 ls -l 命令可以查看文件的具体属性: 如图所示,第一列所示告诉了用户一个文件的类型和权限信息: 1)第一个字符 "d",表明该文件是一个目录文件: 2)r ...

  8. which命令详解

    基础命令学习目录首页 原文链接:https://www.cnblogs.com/jkin/p/10289085.html Linux which命令用于查找文件. which指令会在环境变量$PATH ...

  9. redis使用哈希槽实现集群

    Redis Cluster集群 一.redis-cluster设计 Redis集群搭建的方式有多种,例如使用zookeeper等,但从redis 3.0之后版本支持redis-cluster集群,Re ...

  10. Python之并发编程-多进程

    目录 一.multiprocessiong模块介绍 二.Process类的介绍 三.进一步介绍(守护进程.锁.队列.管道.事件等) 1.守护进程 2.锁(同步锁.互斥锁) 3.信号量(了解) 4.队列 ...