T1:赌徒
链接:https://www.luogu.org/problem/T92080
$sol1:$设$f[i][j]$表示目前得到了$x$分,此时骰子面朝$i$,那么$f[i+k][k]=max(f[i][j]+1,f[i+k][k])$。
先预处理之后直接查询即可。
总复杂度$O(n)$。
$sol2:$考虑最小步数,则先去用$5,6$使之后次数更小,然后用其他面去凑,注意$7$的特判。
代码$(sol1)$:
#include <bits/stdc++.h>
const int MAXN = 1000001;
const int INF = 1 << 30;
using namespace std;
int f[MAXN][10], t, n;
void init() {
memset(f, 127, sizeof(f));
f[0][1] = 0;
for(int i = 0; i <= MAXN; i++) {
for(int j = 1; j <= 6; j++) {
for(int k = 1; k <= 6; k++) {
if(j != k && j + k != 7)
f[i + k][k] = min(f[i + k][k], f[i][j] + 1);
}
}
}
}
int dp(int x) {
int ans = INF;
for(int i = 1; i <= 6; i++)
ans = min(ans, f[x][i]);
return ans;
}
int main() {
cin >> t;
init();
while(t--) {
cin >> n;
cout << dp(n) << endl;
}
return 0;
}
T2:盒子
链接:https://www.luogu.org/problem/T92083
$sol:$设$A[l][r]$表示人处于先手,在$l,r$的范围内进行选择,得到的最优答案,反之$B[l][r]$表示人处于后手......
那么一个人得到的最大的分即可以表示为$f[l][r]=max(A[l+1][r]+a[l],B[l][r-1]+a[r])$。
对于$B[l][r]$,因为是后手,所以可以直接求出。
注意要使用记忆化搜索。
最后答案即为$A(1,n)-B(1,n)$。
代码:
#include <bits/stdc++.h>
const int MAXN = 1050;
using namespace std;
int t, n, a[MAXN], sum[MAXN], f[MAXN][MAXN];
int A(int l, int r);
int B(int l, int r);
int B(int l, int r) { return sum[r] - sum[l - 1] - A(l, r); }
int A(int l, int r) {
if(l == r)
return a[l];
if(f[l][r])
return f[l][r];
f[l][r] = max(B(l + 1, r) + a[l], B(l, r - 1) + a[r]);
return f[l][r];
}
void init() {
memset(f, 0, sizeof(f));
}
int main() {
cin >> t;
for(int i = 1; i <= t; i++) {
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
sum[i] = sum[i - 1] + a[i];
}
cout << A(1, n) - B(1, n) << endl;
init();
}
return 0;
}
T3:楼房修建
链接:https://www.luogu.org/problem/P4198
$sol1:$这道题本质就是求区间内的最长斜率上升子序列,考虑用线段树维护。
线段树里维护该区间的斜率最大值,以及仅该区间的最长斜率上升子序列。
斜率的$pushup$很简单,对于后者,考虑到修改一个点,对其左侧的答案没有影响,那我们可以去递归其右子树。
对于右子树,把当前区间从中划分为两部分,若左边区间的斜率最大值都比当前传进来的斜率要小,那么只用去递归右子树。
否则答案即为递归左子树的答案加上$ans[p]-ans[ls]$。
做法很玄学。
$sol2:$分块(咕)
代码$(sol1)$:
#include <bits/stdc++.h>
#define ls p << 1
#define rs p << 1 | 1
const int MAXN = 100050;
using namespace std;
struct node {
double Max;
}a[MAXN << 2];
int n, m, x, y, ans[MAXN << 2];
int read() {
int x = 0;
bool sign = false;
char alpha = 0;
while(!isdigit(alpha)) {
sign |= alpha == '-';
alpha = getchar();
}
while(isdigit(alpha)) {
x = (x << 1) + (x << 3) + (alpha ^ 48);
alpha = getchar();
}
return sign ? -x : x;
}
struct Segment_Tree {
void push_up(int p) { a[p].Max = max(a[ls].Max, a[rs].Max); }
int query(int p, int l, int r, double k) {
if(a[p].Max <= k)
return 0;
if(l == r)
return a[p].Max > k;
int mid = (l + r) >> 1;
if(a[ls].Max <= k)
return query(rs, mid + 1, r, k);
else
return query(ls, l, mid, k) + ans[p] - ans[ls];
}
void update(int p, int l, int r, int x, double k) {
if(l == r) {
ans[p] = 1;
a[p].Max = k;
return ;
}
int mid = (l + r) >> 1;
if(x <= mid)
update(ls, l, mid, x, k);
if(x > mid)
update(rs, mid + 1, r, x, k);
push_up(p);
ans[p] = ans[ls] + query(rs, mid + 1, r, a[ls].Max);
}
}Tree;
int main() {
n = read();
m = read();
for(int i = 1, x, y;i <= m; i++) {
x = read();
y = read();
Tree.update(1, 1, n, x, (double)y / x);
cout << ans[1] << endl;
}
return 0;
}
T4:弹飞绵羊
$sol1:$分块
(咕)