题解:P9911 [COCI 2023/2024 #2] Kuglice
tiantianyang · · 题解
题目传送门
题目大意
有一个双端队列,你可以在队列头尾拿一个数,如果这个数是第一次拿出来就加一分,最后比分数多少(题意还是很好理解的)。
思路
首先分析大致复杂度
看数据范围
接着找最优策略
很明显总分是一定的,那么我的得分每一次高,最后一定高。
然后算法分析
我们把这一个队列看成一个从
最后具体实现
我们想当总分为
把这个代入到这道题中我们先记录一下有
这个
方程转移式最后就是:
dp[l][r]=max(win(l,r,a[l])-dfs(l+1,r),win(l,r,a[r])-dfs(l,r-1));
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e3+5;
int n,much,maxa;
int a[N],head[N],tail[N],tong[N],dp[N][N];
int win(int l,int r,int val){//判断这个数是否是第一次出现,是的话返回一否则是零
if(head[val]>=l&&tail[val]<=r) return 1;
else return 0;
}
int dfs(int l,int r){//记搜找最优方案
if(l==r) return win(l,r,a[l]); //如果区间只剩一个数,那么直接看这个数满不满足
if(dp[l][r]!=-1) return dp[l][r];//如果已有记录,就直接返回
dp[l][r]=max(-dfs(l+1,r)+win(l,r,a[l]),-dfs(l,r-1)+win(l,r,a[r]));//状态转移
return dp[l][r];
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];//读入
for(int i=1;i<=n;i++){//从前向后看每一个数,第一次出现的位置
if(!tong[a[i]]) head[a[i]]=i;
tong[a[i]]=1;
}
memset(tong,0,sizeof(tong));
for(int i=n;i>=1;i--){//从后向前看每一个数,第一次出现的位置
if(!tong[a[i]]) tail[a[i]]=i,much++;
tong[a[i]]=1;
}
memset(dp,-1,sizeof(dp));//先给出初值
maxa=dfs(1,n);
cout<<(much+maxa)/2<<":"<<(much-maxa)/2;//算出我的和对方的分数
return 0;
}//完美收官