Array Without Local Maximums
算法:动态规划
简要思路:
考虑左边的数跟当前位置的关系,不难想到只有三种情况:大于,小于,等于。 于是可以得到状态 f [ i ] [ j ] [ 0 / 1 / 2 ] f[i][j][0/1/2] f[i][j][0/1/2]表示当前位置填i,左边的数比他……的方案数,由于内存限制,要开滚动数组。分别转移即可。
总结:虽然这道题的一个数字涉及到左右两个数字之间的关系,但是由于动态规划的无后效性,我们只关心他之前的数跟当前数的关系。转移的时候利用限制条件转移即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5+10;
const int p = 998244353;
int n;
int a[N];
int f[2][300][4];
signed main(){
cin>>n;
for (int i = 1; i <= n; i++) cin>>a[i];
int k = 0;
if (a[1] == -1)
for (int j = 1; j <= 200; j++) f[k][j][0] = 1;
else f[k][a[1]][0] = 1;
for (int i = 2; i <= n; k^=1,i++){
int kk = k^1;
int s = 0;
for (int j = 1; j <= 200; j++){
f[kk][j][0]=(a[i]==-1||a[i]==j)?s:0;
(s+=f[k][j][0]+f[k][j][1]+f[k][j][2])%=p;
}
s = 0;
for (int j = 1; j <= 200; j++)
if (a[i] == -1 || a[i] == j)f[kk][j][1] = (f[k][j][0]+f[k][j][1]+f[k][j][2])%p;
else f[kk][j][1] = 0;
s = 0;
for (int j = 200; j >= 1; j--){
f[kk][j][2]=(a[i]==-1||a[i]==j)?s:0;
(s+=f[k][j][1]+f[k][j][2])%=p;
int ans = 0;
for (int i = 1; i <= 200; i++)
ans = (ans+f[k][i][1]+f[k][i][2])%p;
cout<<ans<<endl;
return 0;
}
How many trees?
算法:动态规划
简要思路:
这道题两个限制条件:一个节点数,一个高度。
节点数和高度都是递增的,显然我们可以将这两个设定为状态。
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示i个节点,组成高度不超过j的树的方案数。
由于这是一颗二叉树,所以我们可以分别看左右子树的状态(其实就是一个简化的树上背包),而后用乘法原理进行计算。
f
[
i
]
[
h
]
=
∑
f
[
j
]
[
h
−
1
]
∗
f
[
i
−
j
−
1
]
[
h
−
1
]
f[i][h]=\sum f[j][h-1]*f[i-j-1][h-1]
f[i][h]=∑f[j][h−1]∗f[i−j−1][h−1]
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100;
int n,h;
int f[N][N];
signed main(){
cin>>n>>h;
for (int i = 0; i <= n; i++) f[0][i] = 1;
for (int he = 1; he <= n; he++)
for (int i = 1; i <= n; i++)
for (int j = 0; j < i; j++)
f[i][he]+=f[j][he-1]*f[i-j-1][he-1];
cout<<(f[n][n]-f[n][h-1]);
return 0;
}
Pencils and Boxes
算法:动态规划;单调队列(单调性);前缀和
简要思路:
这种分组划分问题不难想到dp
f
i
f_i
fi表示以i位置结尾的划分是否可行
不难想到可以先将a数组排序,然后按照以下转移
f
i
∣
=
f
j
(
a
[
i
]
−
a
[
j
]
<
=
d
,
j
<
=
i
−
k
)
f_i |=f_j(a[i]-a[j]<=d,j<=i-k)
fi∣=fj(a[i]−a[j]<=d,j<=i−k)
可以看出j的变化范围是一个区间
[
l
,
i
−
k
]
[l,i-k]
[l,i−k],l的位置就是满足上述条件的最小的位置,不难发现l具有单调性,因此可以单调维护(我用二分不知道为啥寄了)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5+100;
int f[N];
int n,k,d;
int a[N];
int sum[N];
signed main(){
cin>>n>>k>>d;
for (int i = 2; i <= n+1; i++) cin>>a[i];
sort(a+2,a+n+2);
f[1] = 1; sum[1] = 1;
for (int i = 1; i <= k; i++) sum[i] = 1;
if (a[k+1]-a[2] > d){
cout<<"NO"<<endl;
return 0;
}
f[k+1] = 1; sum[k+1] = 2;
int now = 1;
for (int i = k+1; i <= n+1; i++){
int r = i-k;
if (a[i]-a[r+1] > d){
f[i] = 0; sum[i] = sum[i-1]; continue;
}
while (a[i]-a[now+1]>d && now <= n+1) now++;
if (now <= r) f[i] = ((sum[r]-sum[now-1]) > 0);
sum[i] = sum[i-1]+f[i];
}
if (f[n+1]) cout<<"YES"; else cout<<"NO";
return 0;
}