[codeforces 1291C] Mind Control 可控选最大+不可控选最小+有一点点象滑动窗口
总目录详见https://blog.csdn.net/mrcrack/article/details/103564004
在线测评地址https://codeforces.com/contest/1291/problem/C
Problem | Lang | Verdict | Time | Memory |
---|---|---|---|---|
C - Mind Control | GNU C++11 | Accepted | 31 ms | 0 KB |
思路同https://www.cnblogs.com/stelayuri/p/12254484.html
题目大意:
总共有n个人和n个数字
n个人拍成一队,n个数字也是有顺序的
你排在第m个位置
按照顺序的每个人可以拿走这个序列中的第一个数字或者最后一个数字
你可以在所有人操作开始前说服最多k个人
让他们固定拿这个序列的第一个或者是最后一个数字
问你在所有可能的情况中可以拿到的数字的最大值中的最小值(即,到你取得的时候,首尾两个数字你总是会取最大的那个,问这些数字中的最小值)
※所有没有被说服的人拿数字是随机拿的,不存在博弈论中的什么总是拿最大或最小
解题思路:
因为只有说服排在自己前面的人才有用
所以可以先让 k=min(k,m-1)
每次轮到自己时,场上还会剩下n-m+1个数字,所以先令len=n-m+1
每次在自己前面的人里至少会有m-k-1个人是不受控制的,令rand=m-k-1
先预处理出最后可能的答案所在区间的答案
即取一段长度为len的区间,答案即它的左端点与右端点中的较大值
总共会有n-len+1个区间,即m个区间,将答案存在数组dm中待调用
然后,最多可以说服k个人,贪心可得越多人能被说服则对答案贡献更大
又因为除了这些被说服的人外,其他排在自己前面的人没被说服的(随机的)人的操作方式是不可预判的!!!
所以,这些被说服的人最好能先取,才能让整个局面更能被自己掌握(确信嘿嘿嘿)
综上,就可以开始枚举这k个人里,有i个人取了这个数列的前端,那么就有k-i个人取了后端
则循环 i=0~k,i 表示有i个人取了前端
那么,这样取的话最后答案区间的左边界就会变成 [i+1,n-i] ,总共会出现rand+1个len长度的可能答案
又因为前面预处理了最后可能的答案
所以最终再循环一次 j=i+1~i+1+rand,j表示可选区间的左边界
取这些答案中的最小值mn
所以这就是这一遍的答案
又因为说服的那些人怎么操作是可控的
所以最终的答案是所有mn中的最大值!!!
(暴力吧,很快的)
//可控选最大+不可控选最小+有一点点象滑动窗口
//轮到主人公准备选时的数据:已选的人数是m-1,len此时还剩的人数n-(m-1);
//轮到主人公准备选时的数据:已选的人数(m-1)中可控人数是k=min(k,m-1)
//轮到主人公准备选时的数据:已选的人数(m-1)中不可控人数是rand=(m-1)-k;
//轮到主人公准备选时的数据:可选区间左边界a[i-len+1],可选区间右边界a[i]
#include <stdio.h>
#define maxn 3510
int a[maxn],dm[3510],n,m,k;
int max(int a,int b){
return a>b?a:b;
}
int min(int a,int b){
return a<b?a:b;
}
int main(){
int t,len,rand,mn,i,j,ans;
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m,&k);
len=n-(m-1),k=min(k,m-1),rand=(m-1)-k,ans=0;
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
if(i>=len)dm[i-len+1]=max(a[i-len+1],a[i]);//主人公选,故选最大
}
for(i=0;i<=k;i++){//i表示可控人员在队伍的开头选物品的人数:i=0表示开始选了0个,那么尾巴选了k个
mn=1e9;
for(j=i+1;j<=i+1+rand;j++)//开头最少可取走0=0+0,故起始位置是0+1;开头最多可取走k+rand,故起始位置是k+rand+1;j表示,主人公即将面对区间的开始位置
mn=min(mn,dm[j]);//因是不可控人员的选择,故结果是糟糕的,故选最小
ans=max(ans,mn);//因k个人可控,故在这些情况中,选最好的结果,故选最小。
}
printf("%d\n",ans);
}
return 0;
}