【 原 题 链 接 】 : \color{blue}{【原题链接】:} 【原题链接】: 点此进入原题面(英文题面)
【 题 目 大 意 】 : \color{blue}{【题目大意】:} 【题目大意】: A
和B
在玩游戏。他们面前有一个数组。他们只能拿最左边或最右边的数,然后加进他们的得分,得分越大越好。他们都会以最优的方法拿,求最后的分数差。(感谢rxz老师
的翻译)
【 思 路 】 : \color{blue}{【思路】:} 【思路】: 记 A l , r A_{l,r} Al,r表示先手在区间 [ l , r ] [l,r] [l,r]内的得分, B l , r B_{l,r} Bl,r表示后手在区间 [ l , r ] [l,r] [l,r]内的得分, W i W_i Wi表示第 i i i个数, s u m l , r sum_{l,r} suml,r表示区间 [ l , r ] [l,r] [l,r]内数的总和。所以,我们有:
A l , r = max { W l + B l + 1 , r , W r + B l , r − 1 } A_{l,r}=\max \{W_l+B_{l+1,r},W_r+B_{l,r-1} \} Al,r=max{Wl+Bl+1,r,Wr+Bl,r−1}
B l , r = s u m l , r − A l , r B_{l,r}=sum_{l,r}-A_{l,r} Bl,r=suml,r−Al,r
我来解释一下它们的含义:第一条转移式比较难理解,为什么前面是 A A A而后面就成了 B B B了呢?不是同一个人吗?其实不然, A A A和 B B B的定义并不是针对某个人的,仅仅是先手后手。什么意思?就是任何一个人都可以调用 A A A或 B B B来求出自己的得分。
那第一条转移式是什么意思呢?就是说, W l + B l + 1 , r W_l+B_{l+1,r} Wl+Bl+1,r即先手取了第 l l l个数,然后A
和·B
就只能在区间 [ l + 1 , r ] [l+1,r] [l+1,r]中取数,而因为轮到其它人选了,所以在区间 [ l + 1 , r ] [l+1,r] [l+1,r]中他是后手,所以是 B l + 1 , r B_{l+1,r} Bl+1,r。 W r + B l , r − 1 W_r+B_{l,r-1} Wr+Bl,r−1类似。
B B B的转移比较简单,因为后手没有什么自动权,所以他的得分就是区间得分总和 − - −先手的得分。
于是,我们用一个递归 + + +两函数互相调用就可以求出答案。答案即 A 1 , n − B 1 , n A_{1,n}-B_{1,n} A1,n−B1,n。
【 代 码 】 : \color{blue}{【代码】:} 【代码】:
int a[1010][1010],test_number,n;
int b[1010][1010],pre[1010],w[1010];
bool va[1010][1010],vb[1010][1010];
inline void init_the_array(){
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(w,0,sizeof(w));
memset(pre,0,sizeof(pre));
memset(va,false,sizeof(va));
memset(vb,false,sizeof(vb));
}//初始化数组
inline void read_the_data(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&w[i]);
pre[i]=pre[i-1]+w[i];
}
}//读入数据以求解
inline int sum(int l,int r){
return pre[r]-pre[l-1];
}//利用前缀和O(1)求[l,r]的分数和
inline int A(int l,int r);
inline int B(int l,int r);
inline int A(int l,int r){
if (l==r) return w[l];
if (va[l][r]) return a[l][r];
a[l][r]=max(w[l]+B(l+1,r),w[r]+B(l,r-1));
va[l][r]=true;return a[l][r];
}//计算先手在区间[l,r]内的得分
inline int B(int l,int r){
if (vb[l][r]) return b[l][r];
b[l][r]=sum(l,r)-A(l,r);
vb[l][r]=true;return b[l][r];
}//计算后手在区间[l,r]内的得分
inline void calc_the_answer(){
init_the_array();
read_the_data();
printf("%d\n",A(1,n)-B(1,n));
}
int main(){
scanf("%d",&test_number);
while (test_number--)
calc_the_answer();
return 0;
}
*****************************
语言:C++
状态:Accepted
得分:100分
备注:无头文件和快读read()函数
*****************************
【 原 题 链 接 】 : \color{blue}{【原题链接】:} 【原题链接】: 点此进入原题面(英文题面)
【 题 目 大 意 】 : \color{blue}{【题目大意】:} 【题目大意】: 我们有一个剑客,需要给对手 n n n点伤害。他每 x x x秒可以给 1 1 1点伤害。有 m m m种技能,需要 b i b_i bi个金币,可把间隔 x x x改为 a i a_i ai (他最多只能学 1 1 1种技能)。
除此之外,现场有 k k k个群众,他需要 d i d_i di个金币,可以给出 c i c_i ci点伤害(剑客只能雇 1 1 1个群众)。
求剑客最少要多少时间杀死对手。
【 思 路 】 : \color{blue}{【思路】:} 【思路】: 若有两个群众a
和b
,若a
需要的金币比b
少,且a
的伤害比b
多,那么我们把b
删去(因为a
比他更物美价廉),剩下的群众满足需要金币越多,伤害越大。
枚举学哪个技能,剩下的钱记为 x x x。二分群众中需要的金币小于等于 x x x且最大的群众(他在可以雇佣的前提下,伤害最大),我们在学习一次技能的前提下,雇佣他。
于是,我们可以枚举我们学习哪个技能,然后通过二分计算我们可以雇佣哪个群众,然后计算去最优值。
注意,可以不学技能,这样可能也最优(毕竟没花钱)!!!当然,也可以不选群众。
题目就做完了……
【 代 码 】 : \color{blue}{【代码】:} 【代码】:
typedef long long ll;
const int N=1e5+100;
struct hero_id{
ll c,d;
bool operator < (hero_id p) const{
return d<p.d;
}
hero_id(ll _c=0,ll _d=0){c=_c;d=_d;}
}h[N];ll a[N],b[N];
vector<hero_id> chose;
ll n,m,k,x,s,ans;
void read_the_data(){
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(h,0,sizeof(h));
chose.clear();//int i;
n=read();m=read();k=read();
x=read();s=read();
for(int i=1;i<=m;i++)
a[i]=read();
for(int i=1;i<=m;i++)
b[i]=read();
for(int i=1;i<=k;i++)
h[i].c=read();
for(int i=1;i<=k;i++)
h[i].d=read();
}
void choose_heros(){
sort(h+1,h+k+1);
chose.push_back(hero_id(0,0));
register ll maxn=0ll;
for(int i=1;i<=k;i++){
if (h[i].c<=maxn) continue;
chose.push_back(h[i]);maxn=h[i].c;
}
}
inline ll Left(ll p){
return (*(--upper_bound(chose.begin(),chose.end(),hero_id(0,p)))).c;
}
inline void calc_answer(){
ans=9e18;a[0]=x;b[0]=0;
for(int i=0;i<=m;i++)
if (s>=b[i]){
ll used=Left(s-b[i]),cost;
if (n-used<=0) cost=0ll;
else cost=(n-used)*a[i];
ans=min(ans,cost);
}
}
int test_number;
int main(){
test_number=read();
while (test_number--){
read_the_data();
choose_heros();
calc_answer();
printf("%lld\n",ans);
}
return 0;
}
*****************************
语言:C++
状态:Accepted
得分:100分
备注:无头文件和快读read()函数
*****************************