本文共 17362 字,大约阅读时间需要 57 分钟。
用我的计算几何模的板码
#includeusing namespace std;using ll = long long;using pii = pair ;using pll = pair ;const int maxn = 5e4 + 5;struct Coor{ ll x, y; Coor operator- (const Coor &ot) const &{ return Coor{ x-ot.x, y-ot.y}; } ll operator^ (const Coor &ot) const &{ return x*ot.y - y*ot.x; } double getk(){ return double(y) / x; }}sta[maxn];ll dp[maxn], sum[maxn], n, L;ll A(int i){ return i + sum[i];}ll B(int j){ return j + L + 1 + sum[j];}ll gety(int j){ return dp[j] + B(j) * B(j);}int main(){ cin >> n >> L; for(int tv, i = 1; i <= n; ++i){ cin >> sum[i]; sum[i] += sum[i - 1]; } dp[0] = 0; sta[0] = Coor{ B(0), dp[0] + B(0)*B(0)}; int tail = 0, head=0; for(int i=1;i<=n;++i){ while(head head && ((sta[tail] - sta[tail-1]) ^ (now - sta[tail])) < 0)tail--; sta[++tail] = now; } cout << dp[n] << endl;}
转载自
这题比上一题多加了个限制,必须最后要分成了 m m m段。所以要加多一个维度,第 i i i段的要在第 i − 1 i-1 i−1段上操作。所以上面斜率优化DP三步走的操作上有点不同,更新 f [ i ] [ j ] f[i][j] f[i][j]时,应该把 f [ i − 1 ] [ j ] f[i-1][j] f[i−1][j]的对应坐标插入凸包中,已方便更新 f [ i ] [ j + 1 ] f[i][j+1] f[i][j+1]。#includeusing namespace std;using ll = long long;using ld = long double;using pii = pair ;using pll = pair ;const int maxn = 3e3 + 5;const ll mod = 1e9 + 7;struct Coor{ ll x, y; ll operator^ (const Coor & ot) const & { return x*ot.y - y*ot.x; } Coor operator- (const Coor & ot) const & { return Coor{ x-ot.x, y-ot.y}; } ld k(){ return ld(y) / x; }}sta[maxn];ll n, m, a[maxn], pre[maxn], f[maxn][maxn], sum = 0, head, tail;ll getY(int k, int j){ return f[k][j] + pre[j] * pre[j];}ll getX(int j){ return pre[j];}int main(){ ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> n >> m; for(int i=1;i<=n;++i){ cin >> a[i]; pre[i] = sum += a[i]; } for(int j=1;j<=n;++j){ f[1][j] = pre[j] * pre[j]; } for(int i=2;i<=m;++i){ head = tail = 0; for(int j=1;j<=n;++j){ while(head < tail && (sta[head+1] - sta[head]).k() <= 2*pre[j])head++; f[i][j] = sta[head].y - 2*pre[j]*sta[head].x + pre[j]*pre[j]; Coor now = Coor{ pre[j], getY(i-1, j)}; while(head < tail && ((sta[tail] - sta[tail-1]) ^ (now - sta[tail])) <= 0)tail--; sta[++tail] = now; } } cout << m*f[m][n] - sum * sum << endl;}
适用于
f ( x ) = min k = b [ x ] x − 1 { g ( k ) + w [ x ] } , 其 中 b [ x ] 随 x 不 降 f(x)=\min_{k=b[x]}^{x-1}\{g(k)+w[x]\},其中b[x]随x不降 f(x)=k=b[x]minx−1{ g(k)+w[x]},其中b[x]随x不降 对于每一个状态f(x),计算过程如下参考自、
#includeusing namespace std;using ll = long long;using pii = pair ;using pll = pair ;const int maxn = 2e5 + 5, maxm = 3e2 + 5;ll n, m, d;int a[maxm], b[maxm], t[maxm];ll f[2][maxn];int main(){ ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> n >> m >> d; for(int i=0;i > a[i] >> b[i] >> t[i]; } for(int j=1;j<=n;++j){ f[0][j] = b[0] - abs(a[0] - j); } for(int i=1;i
#include定义 f [ i ] [ j ] f[i][j] f[i][j]为第 i i i天拥有 j j j股股票的变现收益 f [ i ] [ j ] = m a x { f [ i − 1 ] [ j ] 今 天 不 操 作 − A P [ i ] ∗ j , j < A S [ i ] 第 一 次 买 入 f [ i − w − 1 ] [ x ] + ( x − j ) A P [ i ] , j − A S [ i ] ≤ x ≤ j 今 天 买 入 f [ i − w − 1 ] [ x ] + ( x − j ) B P [ i ] , j ≤ x ≤ j + B S [ i ] 今 天 卖 出 f[i][j]=max\begin{cases} f[i-1][j] &今天不操作\\ -AP[i]*j,\ j<AS[i] &第一次买入\\ f[i-w-1][x]+(x-j)AP[i],\ j-AS[i]\le x \le j &今天买入\\ f[i-w-1][x]+(x-j)BP[i],\ j\le x \le j+BS[i] &今天卖出 \end{cases} f[i][j]=max⎩⎪⎪⎪⎨⎪⎪⎪⎧f[i−1][j]−AP[i]∗j, j<AS[i]f[i−w−1][x]+(x−j)AP[i], j−AS[i]≤x≤jf[i−w−1][x]+(x−j)BP[i], j≤x≤j+BS[i]今天不操作第一次买入今天买入今天卖出 买入和卖出可以变换成 { g ( x ) + w [ j ] } \{g(x)+w[j]\} { g(x)+w[j]}的形式,而x的窗口随着 j j j增加。所以可以单调队列优化。 f [ i ] [ j ] = m a x { f [ i − 1 ] [ j ] 今 天 不 操 作 − A P [ i ] ∗ j , j < A S [ i ] 第 一 次 买 入 ( f [ i − w − 1 ] [ x ] + x ⋅ A P [ i ] ) − j ⋅ A P [ i ] , j − A S [ i ] ≤ x ≤ j 今 天 买 入 ( f [ i − w − 1 ] [ x ] + x ⋅ B P [ i ] ) − j ⋅ B P [ i ] , j ≤ x ≤ j + B S [ i ] 今 天 卖 出 f[i][j]=max\begin{cases} f[i-1][j] &今天不操作\\ -AP[i]*j,\ j<AS[i] &第一次买入\\ (f[i-w-1][x]+x\cdot AP[i])-j\cdot AP[i],\ j-AS[i]\le x \le j &今天买入\\ (f[i-w-1][x]+x\cdot BP[i])-j\cdot BP[i],\ j\le x \le j+BS[i] &今天卖出 \end{cases} f[i][j]=max⎩⎪⎪⎪⎨⎪⎪⎪⎧f[i−1][j]−AP[i]∗j, j<AS[i](f[i−w−1][x]+x⋅AP[i])−j⋅AP[i], j−AS[i]≤x≤j(f[i−w−1][x]+x⋅BP[i])−j⋅BP[i], j≤x≤j+BS[i]今天不操作第一次买入今天买入今天卖出#define ne(__x) cout << #__x << ": " << __x << endl;#define ns(__x) cout << #__x << ": " << __x << " ";#define ps(__x) cout << __x << " ";#define pe(__x) cout << __x << endl;using namespace std;using ll = long long;using pii = pair ;using pll = pair ;const int maxn = 2e5 + 5, maxm = 3e2 + 5;int n, m, x, y, k;int s[205], t[205], d[205];int f[2][205][205];char a[205][205];int main(){ ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> n >> m >> x >> y >> k; for(int i=1;i<=n;++i){ for(int j=1;j<=m;++j){ cin >> a[i][j]; } } for(int i=1;i<=k;++i){ cin >> s[i] >> t[i] >> d[i]; } memset(f, -1, sizeof(f)); f[0][x][y] = 0; auto f0 = f[0], f1 = f[1]; for(int q=1;q<=k;++q){ int dt = t[q] - s[q] + 1; if(d[q]==1){ for(int j=1;j<=m;++j){ deque Q; for(int i=n;i>=1;--i){ if(a[i][j] == 'x'){ f1[i][j] = -1; Q.clear(); continue;} while(!Q.empty() && f0[Q.back()][j] + Q.back() <= f0[i][j] + i)Q.pop_back(); if(~f0[i][j]) Q.push_back(i); while(!Q.empty() && Q.front() > i + dt) Q.pop_front(); if(Q.empty()) f1[i][j] = -1; else f1[i][j] = f0[Q.front()][j] + Q.front() - i; } } }else if(d[q]==2){ for(int j=1;j<=m;++j){ deque Q; for(int i=1;i<=n;++i){ if(a[i][j] == 'x'){ f1[i][j] = -1; Q.clear(); continue;} while(!Q.empty() && f0[Q.back()][j] - Q.back() <= f0[i][j] - i)Q.pop_back(); if(~f0[i][j]) Q.push_back(i); while(!Q.empty() && Q.front() < i - dt) Q.pop_front(); if(Q.empty()) f1[i][j] = -1; else f1[i][j] = f0[Q.front()][j] + i - Q.front(); } } } else if(d[q]==3){ for(int i=1;i<=n;++i){ deque Q; for(int j=m;j>=1;--j){ if(a[i][j] == 'x'){ f1[i][j] = -1; Q.clear(); continue;} while(!Q.empty() && f0[i][Q.back()] + Q.back() <= f0[i][j] + j)Q.pop_back(); if(~f0[i][j]) Q.push_back(j); while(!Q.empty() && Q.front() > j + dt) Q.pop_front(); if(Q.empty()) f1[i][j] = -1; else f1[i][j] = f0[i][Q.front()] + Q.front() - j; } } }else if(d[q]==4){ for(int i=1;i<=n;++i){ deque Q; for(int j=1;j<=m;++j){ if(a[i][j] == 'x'){ f1[i][j] = -1; Q.clear(); continue;} while(!Q.empty() && f0[i][Q.back()] - Q.back() <= f0[i][j] - j)Q.pop_back(); if(~f0[i][j]) Q.push_back(j); while(!Q.empty() && Q.front() < j - dt) Q.pop_front(); if(Q.empty()) f1[i][j] = -1; else f1[i][j] = f0[i][Q.front()] + j - Q.front(); } } } swap(f0, f1); } int ans = 0; for(int i=1;i<=n;++i){ for(int j=1;j<=m;++j){ ans = max(ans, f0[i][j]); // ps(f0[i][j]); } // cout << endl; } cout << ans << endl;}
#includeusing namespace std;using ll = long long;using pii = pair ;using pll = pair ;const int maxn = 2e5 + 5, maxm = 3e2 + 5;ll T, MaxP, w;ll ap[2005], bp[2005], as[2005], bs[2005];ll f[2005][2005];int main(){ ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); memset(f, 0x80, sizeof(f)); cin >> T >> MaxP >> w; for(int i=1;i<=T;++i){ cin >> ap[i] >> bp[i] >> as[i] >> bs[i]; bs[i] = min(bs[i], MaxP); as[i] = min(as[i], MaxP); } for(int j=0;j<=min(MaxP, as[1]);++j){ f[1][j] = -ap[1] * j; } for(int i=2;i<=T;++i){ // for(int j=0;j<=MaxP;++j)// cout << f[i-1][j] << ' ';// cout << endl; deque AQ, BQ; if(i-w-1 > 1){ for(int j=0;j
解决形如
f i , j = min i ≤ k < j f i , k + f k + 1 , j + ω ( i , j ) f_{i, j} = \min_{i\le k < j} f_{i, k} + f_{k+1, j} + \omega(i,j) fi,j=i≤k<jminfi,k+fk+1,j+ω(i,j) 必须满足以下条件:由于 l 1 ≤ l 2 ≤ r 1 ≤ r 2 , ω ( l 1 , r 1 ) + ω ( l 2 , r 2 ) ≤ ω ( l 1 , r 2 ) + ω ( l 2 , r 1 ) l_1\le l_2\le r_1\le r_2,\ \omega(l_1,r_1) + \omega(l_2,r_2) \le \omega(l_1,r_2) + \omega(l_2,r_1) l1≤l2≤r1≤r2, ω(l1,r1)+ω(l2,r2)≤ω(l1,r2)+ω(l2,r1)可以调换一下位置得到
ω ( l 1 , r 1 ) − ω ( l 2 , r 1 ) ≤ ω ( l 1 , r 2 ) − ω ( l 2 , r 2 ) \omega(l_1,r_1) - \omega(l_2,r_1) \le \omega(l_1,r_2) - \omega(l_2,r_2) ω(l1,r1)−ω(l2,r1)≤ω(l1,r2)−ω(l2,r2) 设 g ( r ) = ω ( l 1 , r ) − ω ( l 2 , r ) g(r)=\omega(l_1,r) - \omega(l_2,r) g(r)=ω(l1,r)−ω(l2,r),则有 r 1 < r 2 , g ( r 1 ) ≤ g ( r 2 ) r_1<r_2,\ g(r_1)\le g(r_2) r1<r2, g(r1)≤g(r2). 即当 g ( j ) = ω ( i , j ) − ω ( i + 1 , j ) g(j)=\omega(i,j) - \omega(i+1,j) g(j)=ω(i,j)−ω(i+1,j)满足随 j j j单调不降,则符合四边形不等式。引理1:若满足关于区间包含的单调性的函数 ω ( i , j ) \omega(i,j) ω(i,j)满足区间包含单调性和四边形不等式,则状态 f i , j f_{i,j} fi,j也满足四边形不等式。
若 f f f符合四边形不等式,记 m l , r = min { k : f l , r = g k , l , r } m_{l, r}=\min \left\{k: f_{l, r}=g_{k, l, r}\right\} ml,r=min{ k:fl,r=gk,l,r}表示最优决策点,则有下面两种做法。区间DP用的是第一条。
m l , r − 1 ≤ m l , r ≤ m l + 1 , r m l − 1 , r ≤ m l , r ≤ m l , r + 1 m_{l, r-1} \leq m_{l, r} \leq m_{l+1, r}\\ m_{l-1, r} \leq m_{l, r} \leq m_{l, r+1} ml,r−1≤ml,r≤ml+1,rml−1,r≤ml,r≤ml,r+1 时间复杂度为 O ( n 2 ) O(n^2) O(n2),关键代码:for (int len = 2; len <= n; ++len) // 枚举区间长度 for (int l = 1, r = len; r <= n; ++l, ++r) { // 枚举长度为len的所有区间 f[l][r] = INF; for (int k = m[l][r - 1]; k <= m[l + 1][r]; ++k) if (f[l][r] > f[l][k] + f[k + 1][r] + w(l, r)) { f[l][r] = f[l][k] + f[k + 1][r] + w(l, r); // 更新状态值 m[l][r] = k; // 更新(最小)最优决策点 } }
对于dp转移合法的证明,其实很多时候不用证明,直接打表就行了,比如先跑一个 O ( n 3 ) O(n^3) O(n3)的代码,跑的时候判断是否满足四边形不等式,决策是否单增等等,如果不满足就输出false之类的,或者打一个决策表出来观察,这样其实会省下一部分时间。
题意:把n个节点分成m块,使得最后价值最小。 思路: 这道题推推公式可以用斜率优化做,但是推公式太慢了。很容易得到dp式子, f f f表示炸了 i i i次,到 j j j点的最小价值。 w ( i , j ) w(i,j) w(i,j)是 i i i到 j j j为一块的代价,可以 O ( n 2 ) O(n^2) O(n2)获得。打表验证 w ( i , j ) w(i,j) w(i,j)四边形不等式,直接优化。 f [ i ] [ j ] = min i ≤ k < j f [ i − 1 ] [ k ] + w [ k + 1 ] [ j ] f[i][j]=\min_{i\le k<j} f[i-1][k] + w[k+1][j] f[i][j]=i≤k<jminf[i−1][k]+w[k+1][j]回顾一下四边形优化。首先,他的使用条件是dp数组是由一个满足四边形不等式的w数组相加得来的,即dp数组也满足四边形不等式。这样的话,对于当前状态i、j,dp[i][j]取最有的决策,就在s[i-1][j]到s[i,j-1]之间。如此一来,决策的枚举数量大大减少,达到了优化的效果。
(不是很理解)
#include题意:建立 p p p个邮局,选择他们建造的位置,使每个村庄与其最近的邮局之间的距离总和最小。 思路: f [ i ] [ j ] f[i][j] f[i][j]表示 i i i个邮局,前 j j j个点的总代价。 w [ k ] [ j ] w[k][j] w[k][j]表示此区间内做一个邮局的最小代价,其实这个邮局必然在中位数那个点上。同样,打表验证四边形不等式(看注释部分)。 f [ i ] [ j ] = min i ≤ k < j f [ i − 1 ] [ k ] + w [ k + 1 ] [ j ] f[i][j]=\min_{i\le k<j} f[i-1][k] + w[k+1][j] f[i][j]=i≤k<jminf[i−1][k]+w[k+1][j]#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);using namespace std;const int maxn = 1e3 + 5;int a[maxn], p[maxn][maxn], pre[maxn], f[maxn][maxn], mt[maxn][maxn];int g(int i, int j){ return p[i-1][j] - p[i][j];}int main(){ IOS; int n, m; while(cin >> n >> m && n && m){ for(int i = 1; i <= n; ++i){ // a[i] = rand() % 10 + 1; cout << a[i] << endl; cin >> a[i]; pre[i] = pre[i - 1] + a[i]; } for(int i = 1; i <= n; ++i){ for(int j = i + 1; j <= n; ++j){ p[i][j] = p[i][j-1] + (pre[j - 1] - pre[i - 1]) * a[j]; } } memset(f, 0x7f, sizeof(f)); for(int i = 0; i <= n; ++i){ f[0][i] = p[1][i]; mt[0][i] = 0; } for(int i = 1; i <= m; ++i){ mt[i][n + 1] = n - 1; for(int j = n; j >= i; --j){ for(int k = mt[i-1][j]; k <= mt[i][j+1]; ++k){ if(f[i - 1][k] + p[k + 1][j] < f[i][j]){ f[i][j] = f[i - 1][k] + p[k + 1][j]; mt[i][j] = k; } } } } cout << f[m][n] << endl; }}
#include// #define endl '\n'#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);using namespace std;const int N = 3e3 +5;const double eps = 1e-10;using LL = long long;LL pos[N], sp[N], f[N][N], mt[N][N];LL w(int k, int j){ int mid = k + j >> 1; return (2 * mid + 1 - j - k) * pos[mid] + (sp[j] + sp[k - 1] - 2 * sp[mid]);}//LL g(int i, int j){ // return w(i, j) - w(i + 1, j);//}int main(){ IOS; int n, p; cin >> n >> p; for(int i = 1; i <= n; ++i){ cin >> pos[i]; } sort(pos + 1, pos + n + 1); for(int i = 1; i <= n; ++i){ sp[i] = sp[i - 1] + pos[i]; }// for(int i = 1; i < n; ++i){ // for(int j = i; j <= n; ++j){ // cout << g(i, j) << " ";// }// cout << endl;// } memset(f, 0x7f, sizeof(f)); for(int j = 1; j <= n; ++j){ mt[0][j] = 0; } f[0][0] = 0; for(int i = 1; i <= p; ++i){ mt[i][n + 1] = n - 1; for(int j = n; j; --j){ for(int k = mt[i - 1][j]; k <= mt[i][j + 1]; ++k){ LL now = f[i - 1][k] + w(k + 1, j); if(f[i][j] > now){ f[i][j] = now; mt[i][j] = k; } } } } cout << f[p][n] << endl;}
解决以下形式的问题
f r = min l = 1 r − 1 { f l + w ( l , r ) } ( 1 ≤ r ≤ n ) f_{r}=\min _{l=1}^{r-1}\left\{f_{l}+w(l, r)\right\} \quad(1 \leq r \leq n) fr=l=1minr−1{ fl+w(l,r)}(1≤r≤n)若 w ( l , r ) w(l, r) w(l,r)符合四边形不等式,则 ∀ 1 ≤ a ≤ b ≤ c ≤ d ≤ N , w a , c + w b , d ≤ w a , d + w b , c \forall 1 \leq a \leq b \leq c \leq d \leq N, w_{a, c}+w_{b, d} \leq w_{a, d}+w_{b, c} ∀1≤a≤b≤c≤d≤N,wa,c+wb,d≤wa,d+wb,c。当 w a , c ≥ w b , c w_{a, c} \geq w_{b, c} wa,c≥wb,c,由上式可得 w a , d ≥ w b , d w_{a, d} \geq w_{b, d} wa,d≥wb,d,即
∀ i ≤ j : 最 优 决 策 点 k i ≤ k j \forall i \leq j: 最优决策点k _i \leq k_j ∀i≤j:最优决策点ki≤kj