一天有 n n n 个小时,在第 i i i 个小时睡觉恢复体力 u i u_i ui。一头牛一天要休息 b b b 个小时,可以分成多段。每一段需要花费一个小时才能睡着,这一个小时不恢复体力。询问恢复体力的最大值。
可以考虑dp。第一维是每天的时间,第二维是已经休息的时间。转移的时候需要知道当前休息的这一个小时是否可以恢复体力,即是否是一段休息的第一个小时,所以增加第三维。
牛在每一个小时的状态有:清醒,入睡,熟睡。只有熟睡可以恢复体力,入睡和熟睡都是休息。
因为时间是环形的,所以可以通过分类讨论第一个小时的状态,来实现环形dp的转移
注意空间大小,滚动数组优化空间
做题的时候一个不明白的地方是:对于第一种状态,第一个小时为入睡或者清醒,最后一个小时的状态是什么。其实清醒,入睡,熟睡都可以。
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 4e3+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
int n, m, ans;
int a[maxn];
int f[2][maxn][2];
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];
memset(f, -INF, sizeof(f));
int cur = 1, pre = 0;
f[0][0][0] = f[0][1][1] = 0;
for(int i = 2; i <= n; i++){
for(int j = 0; j <= min(m, i); j++){
f[cur][j][0] = max(f[pre][j][0], f[pre][j][1]);
if(j)
f[cur][j][1] = max(f[pre][j-1][0], f[pre][j-1][1]+a[i]);
}
cur ^= 1;
pre ^= 1;
}
ans = max(f[pre][m][0], f[pre][m][1]);
memset(f, -INF, sizeof(f));
cur = 1, pre = 0;
f[0][1][1] = a[1];
for(int i = 2; i <= n; i++){
for(int j = 0; j <= min(m, i); j++){
f[cur][j][0] = max(f[pre][j][0], f[pre][j][1]);
if(j)
f[cur][j][1] = max(f[pre][j-1][0], f[pre][j-1][1]+a[i]);
}
cur ^= 1;
pre ^= 1;
}
ans = max(ans, f[pre][m][1]);
cout << ans << endl;
return 0;
}
环形公路上有 n n n 个仓库,仓库 i , j i,j i,j 之间的距离 d i s t ( i , j ) dist(i,j) dist(i,j) 为顺时针或者逆时针种较近的一种,在 i , j i,j i,j 运输的代价为 d i s t ( i , j ) + a i + a j dist(i,j) + a_i + a_j dist(i,j)+ai+aj。询问在两座仓库之间运送货物的最大代价
对于环形的处理是:断环为链。对于仓库 i , j ( j < i ) i,j (j < i) i,j(j<i) 运输,如果 i − j < N 2 i-j < \frac{N}{2} i−j<2N,则在 i , j i,j i,j 之间运输;如果 i − j > N 2 i-j > \frac{N}{2} i−j>2N,在 i , j + N i,j+N i,j+N 之间运输。
题目转化为:长为 2 n 2n 2n 的链,对于 i i i,找到 j j j,使 a i + i + a j − j a_i+i+a_j-j ai+i+aj−j 最大,即 a j − j a_j-j aj−j 最大 ( i − j < N 2 ) (i-j < \frac{N}{2}) (i−j<2N)
可以用单调队列来维护 a j − j a_j-j aj−j,时间复杂度为 O ( n ) O(n) O(n)
#include
using namespace std;
typedef long long ll;
typedef double db;
#define int ll
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 1e6+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
int n, a[maxn << 1];
int q[maxn << 1], hh, tt;
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
a[i+n] = a[i];
}
q[++tt] = 1;
int len = n >> 1;
int ans = 0;
for(int i = 2; i <= n * 2; i++){
while(hh <= tt && q[hh] < i-len)
hh++;
ans = max(ans, a[i]+i+a[q[hh]]-q[hh]);
while(hh <= tt && a[q[tt]]-q[tt] < a[i]-i)
tt--;
q[++tt] = i;
}
cout << ans << endl;
return 0;
}
机器人初始在 ( x , y ) (x, y) (x,y),每步等概率向左、向右、原地不动、向下移动一格。求到 ( n , m ) (n,m) (n,m) 的步数的期望。
设 f i , j f_{i,j} fi,j 为 ( i , j ) (i,j) (i,j) 走到 ( n , m ) (n,m) (n,m) 步数的期望
对于第一列: f i , 1 = 1 + 1 3 ( f i , 1 + f i + 1 , 1 + f i , 2 ) f_{i,1} = 1 + \frac{1}{3}(f_{i,1}+f_{i+1, 1}+f_{i, 2}) fi,1=1+31(fi,1+fi+1,1+fi,2)
对于第 2~m-1 列: f i , j = 1 + 1 4 ( f i , j + f i , j − 1 + f i , j + 1 + f i + 1 , j ) f_{i,j} = 1+\frac{1}{4}(f_{i,j}+f_{i, j-1}+f_{i, j+1}+f_{i+1, j}) fi,j=1+41(fi,j+fi,j−1+fi,j+1+fi+1,j)
因为不能向上走,所以行间的转移没有后效性,可以线性转移。但行内相互转移,没有递推的顺序,所以需要解方程组,通过高斯消元来解方程组
注意:对于只有一列的情况, 1 2 \frac{1}{2} 21 停在原地, 1 2 \frac{1}{2} 21 向下走一步,所以期望是 2 ( n − x ) 2(n-x) 2(n−x)
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 1e3+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
int n, m, x, y;
double f[maxn][maxn], a[maxn][maxn];
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m >> x >> y;
if(m == 1){
cout << fixed << setprecision(4) << 2.0 * (n - x) << endl;
return 0;
}
for(int i = n-1; i >= x; i--){
a[1][1] = a[m][m] = 2 / 3.0, a[1][m + 1] = 1 + f[i + 1][1] / 3;
a[1][2] = a[m][m - 1] = -1 / 3.0, a[m][m + 1] = 1 + f[i + 1][m] / 3;
for(int j = 2; j <= m-1; j++){
a[j][j - 1] = a[j][j + 1] = -1 / 4.0;
a[j][j] = 3 / 4.0, a[j][m + 1] = 1 + f[i + 1][j] / 4;
}
for(int i = 1; i <= m; i++){
double r = a[i + 1][i] / a[i][i];
a[i + 1][i] = 0, a[i + 1][i + 1] -= r * a[i][i + 1];
a[i + 1][m + 1] -= r * a[i][m + 1];
}
for(int j = m; j >= 1; j--) {
double r = a[j - 1][j] / a[j][j];
a[j - 1][j] = 0;
a[j - 1][m + 1] -= a[j][m + 1] * r;
f[i][j] = a[j][m + 1] / a[j][j];
}
}
cout << fixed << setprecision(4) << f[x][y] << endl;
return 0;
}