2022牛客寒假集训营4——I(dp), A(双指针)

I 爆炸的符卡洋洋洒洒(dp)

题意:

一共 n 张符卡,每张符卡有消耗 ai,威力 bi。
问,保证消耗之和为 k 的倍数的情况下,最大威力为多少? 1 ≤ n, k ≤ 1000

思路:

dp
f[i, j]表示:前i个位置,ai之和%k 为 j 时,最大 bi 和。

对于每个位置,看当前位置选上之后对答案有没有更新。

	for(int i=1;i<=n;i++)
		for(int j=0;j<k;j++)
			f[i, (j+ai)%k] = max(f[i-1, (j+ai)%k], f[i, j]+b[i]).

初始化:
for(int i=1;i<k;i++) f[0, i] = -1e9;
因为是取max,所以一开始第0个位置的状态都赋值为无穷小。(除了 f[0, 0])

Code:

const int N = 2010, mod = 1e9+7;
int T, n, m, k;
int a[N], b[N];
int f[N][N];

signed main(){
	Ios;
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>a[i]>>b[i];
	
	for(int j=1;j<k;j++) f[0][j] = -1e9;
	
	for(int i=1;i<=n;i++)
	{	
		for(int j=0;j<k;j++)
		{
			f[i][(j+a[i])%k] = max(f[i-1][(j+a[i])%k], f[i-1][j]+b[i]);
		}
	}
	
	if(f[n][0]<=0) cout<<-1;
	else cout<<f[n][0];
	
	return 0;
}

R

题意:

给出长度为n的字符串,问一共有多少个连续子串,满足:
子串包含至少 k 个 'R' 字符,且不能包含 'P' 字符。
1 ≤ n ≤ 200000,1 ≤ k ≤ 20;

思路:

遍历每个 R 的位置:
找到从当前位置开始,第 k 个 ‘R’ 的位置 x,第一个 ‘P’ 的位置 y。
如果 y<x 的话,说明不存在连续的子串包含k个字符,不包含’P’字符;
否则,从x到y中的所有位置都可以作为右端点。
为了避免重复计算,左端点为上一个’R’的位置到当前位置,但是仍然不能包含’P’,所以就是当前位置之前的最后一个’P’或’R’的位置到当前位置的位置都可以作为左端点。

因为是从前往后走,所以找当前位置后面的第一个’p’的位置是一直往后走的,可以用双指针维护;
同理,上一个’P’和’R’的位置也可以这样维护。

Code:

const int N = 200010, mod = 1e9+7;
int T, n, m, k;
int a[N], b[N];
char c[N];

signed main(){
	Ios;
	cin>>n>>k;
	
	int cnt1=0, cnt2=0;
	for(int i=1;i<=n;i++){
		cin>>c[i];
		if(c[i]=='R') a[++cnt1]=i, mp[i]=cnt1;
		if(c[i]=='P') b[++cnt2]=i;
	}
	b[++cnt2] = n+1;
	
	int np=0, fp1=0, fp2=0, ans=0, z=0;
	for(int i=1;i<=n;i++)
	{
		if(c[i]!='R') continue;
		int t = mp[i]+k-1;
		if(t>cnt1) continue;
		int x = a[t]; //最后一个R所在的位置 
		
		while(b[np]<i) np++;
		int y = b[np]; //大于当前位置的第一个P所在位置 
		
		if(y<x) continue;
		
		//z: 左边界 
		while(fp1+1<=cnt1 && a[fp1+1]<i) fp1++, z=max(z,a[fp1]);
		while(fp2+1<=cnt2 && b[fp2+1]<i) fp2++, z=max(z,b[fp2]);
		
		ans += (i-z)*(y-x);
	}
	cout<<ans;
	
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值