2020.7.14集训

divide

description
给定一个长度为 n n n的数列,将其划分为三段,问这三段的最大值的最小值

solution
这题方法很多

O ( l o g 2 n ) O(log^2n) O(log2n)
二分答案,里面两次在前缀和上二分找断点,进行判断

O ( n ) O(n) O(n)
用类似于双指针的方法,每次找前面的一个位置,让第一段和第二段的差尽量的小,然后取最小值

O ( n log ⁡ n ) O(n\log n) O(nlogn)
二分答案, O ( n ) O(n) O(n)判断

thd

description
LGTB 最近在玩一个类似 DOTA 的游戏名叫 THD
有一天他在守一座塔,对面的 N N N 个小兵排成一列从近到远站在塔前面
每个小兵有一定的血量 h i h_i hi ,杀死后有一定的金钱 g i g_i gi
每一秒,他都可以攻击任意一个活着的小兵,对其造成 p p p 点伤害,如果小兵的血量低于 1 1 1 点,小兵死亡,他
得到金钱。他也可以不攻击任何小兵。
每一秒 LGTB 攻击完毕之后,塔会攻击距离塔最近的一个活着的小兵,对其造成 q q q 点伤害,如果小兵的血
量低于 1 1 1 点,小兵死亡,LGTB 不会得到金钱
现在 LGTB 想知道,在他选择最优策略时,他能得到多少钱。

solution
发现,如果我们选择打某个小兵,那就一定要保证拿到这个小兵的钱,否则还不如让防御塔把他弄死
发现,只要保证我们打的次数 ≤ \leq 防御塔打的次数+1,顺序是无所谓的
发现,如果我们选择打某个小兵,那么最优策略是让防御塔打到再打一次之后就打死的时候去补刀

所以,我们可以设 f [ i ] [ j ] f[i][j] f[i][j]表示打第 i i i个小兵,比防御塔少打 j j j次的状态
我们分别处理出防御塔把第 i i i个小兵打残的次数 s a k u r a [ i ] sakura[i] sakura[i]
和补第 i i i个小兵的刀的次数 a d c [ i ] adc[i] adc[i]

然后就可以相应转移了

注意两点
1. s a k u r a [ i ] sakura[i] sakura[i] a d c [ i ] adc[i] adc[i]的预处理可能有锅
2.初始的时候,我们应该认为,我们比防御塔少打一次,因为第一次是我们打

#include <bits/stdc++.h>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;

const int N=105;
const int M=30005;

#pragma GCC optimize(2)

template<typename T> void read(T &x){
   x=0;int f=1;
   char c=getchar();
   for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
   for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}

int p,q,n;
int a[N],g[N];
int f[N][M];
int sakura[N],adc[N];
int ans;

int main()
{
	freopen("thd.in","r",stdin);
	freopen("thd.out","w",stdout);
	memset(f,-0x3f,sizeof(f));
	read(p),read(q),read(n);
	Rep(i,1,n)read(a[i]),read(g[i]);
	Rep(i,1,n){
		sakura[i]=(a[i]-1)/q;
		adc[i]=(a[i]-sakura[i]*q-1)/p+1;
	}
	f[0][1]=0;
	Rep(i,1,n)
		Rep(j,0,1001){
			if(j>=sakura[i])f[i][j]=max(f[i][j],f[i-1][j-sakura[i]-1]);
			if(j+adc[i]-sakura[i]<=30000&&j+adc[i]-sakura[i]>=0)f[i][j]=max(f[i][j],f[i-1][j+adc[i]-sakura[i]]+g[i]);	
		}
	Rep(i,0,1001)ans=max(ans,f[n][i]);
	printf("%d\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值