[COCI 2023/2024 #2] Kuglice题解
题面
题目描述
一个双端队列里面有 n 个球,每个球有一个颜色。A 和 B 玩一个游戏:
A 先手,两个人轮流操作,每次从队列的最左端或者最右端拿出一个球,如果这种颜色的球是第一次被拿出,拿出它的人获得 1 分。所有球都拿完后游戏结束。
假设 A 和 B 都以最优策略操作,请求出最终得分是多少。
输入格式
第一行一个整数 n。
第二行 n 个整数 a1∼an 表示从左到右每个球的颜色。
输出格式
输出一行两个以 :
隔开的整数(形如 a:b
),a
表示 A 最终的得分,b
表示 B 最终的得分。
输入输出样例
输入 #1
5 1 1 2 1 1
输出 #1
1:1
输入 #2
6 1 2 3 1 2 3
输出 #2
2:1
说明/提示
数据范围
Subtask | 分值 | 特殊性质 |
---|---|---|
1 | 17 | ai≤2 |
2 | 10 | n≤20 |
3 | 26 | ai≤20 |
4 | 15 | n≤300 |
5 | 42 | 无 |
对于所有数据,1≤n≤3000,1≤ai≤n。
题目大意
很好理解,就是问两个人拿球,谁先拿某种颜色谁加分,求分数。
题目思路
我们可以用区间DP解题。 先设一个数组f[i][j]表示取完区间[i,j]内的球,先手比后手多出来的分数。
再设shu表示总共能得到的分数,那么根据小学知识,先手能得到的总分就是(shu+f[1][n])/2,后手能得到的总分就是shu-(shu+f[1][n])/2。
然后,再设两个数组shou[i]和wei[i],表示i元素出现的最早和最晚位置,因为取i元素只能在这两个位置取。
接下来来看状态转移方程。因为只能在左右两端取球,所以f[i][j]的状态转移只能跟f[i+1][j]和f[i][j-1]有关。
并且,之前说过取i元素只能在这最早和最晚位置取,所以,只要满足shou[a[i]]>=i&&wei[a[i]]<=j,第i个球的颜色就一定只在区间内出现过。
假设先手取走了i号球,那么对于区间[i+1,j],先手就变成了原来的后手,所以应该用取走i号球的得分减去f[i+1][j]。
先手取走了j号球也同理:对于区间[i,j-1],先手也变成了原来的后手,所以应该用取走j号球的得分减去f[i][j-1]。
这样,我们就得到了如下状态转移方程:
f[i][j]=max(cc(i,j,a[i])-f[i+1][j],cc(i,j,a[j])-f[i][j-1]);
其中,cc(i,j,k)表示计算在[i,j]内取走颜色为k的球的得分。
代码见下
#include<bits/stdc++.h>
using namespace std;
long long n,m,a[1000010],shu,shou[1000010],wei[1000010],f[3010][3010];
bool cu[1000010];
bool cc(int i,int j,int k){
if(shou[k]>=i&&wei[k]<=j) return 1;
else return 0;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
if(cu[a[i]]==0){
cu[a[i]]=1;
shu++;
shou[a[i]]=i;
}
wei[a[i]]=i;
}
for(int l=1;l<=n;l++){
for(int i=1;i<=n;i++){
int j=l+i-1;
f[i][j]=max(cc(i,j,a[i])-f[i+1][j],cc(i,j,a[j])-f[i][j-1]);
}
}
cout<<(shu+f[1][n])/2<<":"<<shu-(shu+f[1][n])/2;
return 0;
}