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)判断
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
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;
}