【过题笔记】 7.15

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][h1]f[ij1][h1]

#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<=ik)
可以看出j的变化范围是一个区间 [ l , i − k ] [l,i-k] [l,ik],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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值