P9911 [COCI 2023/2024 #2] Kuglice

题目链接P9911 [COCI 2023/2024 #2] Kuglice

题目大意

n n n 个带有颜色的球,每轮两个人各可以从一端拿一个球,最后求两个人手中不同颜色球的比值

第一行一个整数 n n n
第二行 n n n 个整数 a [ 1 a[1 a[1 ~ n ] n] n] 表示从左到右每个球的颜色

思路

显然,我们可以想到这是一个博弈dp
例如可以定义 d p [ l ] [ r ] [ o p ] dp[l][r][op] dp[l][r][op] 表示在 l l l r r r 的区间内( o p op op 表示先后手)可以取到多少个不同颜色的球
动态转移方程式是什么呢?
在这里插入图片描述

其实可以由 l + 1 l+1 l+1 r r r 的区间加上第 l l l 个数 l l l r − 1 r-1 r1 的区间加上第 r r r 个数 得到
d p [ l ] [ r ] [ o p ] = m a x ( d p [ l − 1 ] [ r ] [ o p ] + dp[l][r][op]=max(dp[l-1][r][op]+ dp[l][r][op]=max(dp[l1][r][op]+ (取第 l l l 个数可不可以加1) , d p [ l ] [ r − 1 ] [ o p ] + , dp[l][r-1][op]+ ,dp[l][r1][op]+ (取第 r r r 个数可不可以加1) ) ) )
所以再加一个记忆化搜索就行了

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,a[3005],ans,sum;
int lt[3005],rt[3005]; //lt[x]=y 表示左边第一个x在y的位置,rt[]同理
int dp[3005][3005][2]; //dp[l][r][op]=x 表示op这个人在l到r的区间内,可以得x分   //op=0先手,op=1后手 
int f(int l,int r,int x){ //根据左边和右边第一个x的位置判断x是否可以加1个
	return (x==lt[a[x]] && r>=rt[a[x]]) || (x==rt[a[x]] && l<=lt[a[x]]); 
	//如果正好是左数第一个且右边第一个没有选到,那么选了可以加1个,右边同理
}
void dfs(int l,int r,int op){
	if(dp[l][r][op]!=-1) return ; //如果已经搜索过了就退出
	if(l==r) 
	{
		dp[l][r][op]=f(l,r,l),dp[l][r][op^1]=0; //如果l=r,那么就看a[l]可以可以加1个
		return ;
	}
	dfs(l+1,r,op^1),dfs(l,r-1,op^1); //往里搜索,先后手交换
	ans=dp[l+1][r][op]+f(l,r,l); //由l+1到r的区间选a[l]转移得到
	sum=dp[l][r-1][op]+f(l,r,r); //由l到r-1的区间选a[r]转移得到
	if(ans>sum) dp[l][r][op]=ans,dp[l][r][op^1]=dp[l+1][r][op^1];  //如果由l+1到r的区间转移过来:那么它的对手也由l+1到r的区间转移过来
	else if(ans<sum) dp[l][r][op]=sum,dp[l][r][op^1]=dp[l][r-1][op^1]; //同上一行
	else dp[l][r][op]=ans,dp[l][r][op^1]=min(dp[l+1][r][op^1],dp[l][r-1][op^1]); //如果一样的,那么就让对手选少的一项
	return ;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) 
	{	
		scanf("%d",&a[i]);
		if(lt[a[i]]==0) lt[a[i]]=i; //如果这是左边第一个a[i],那么记录第一个a[i]在i上
		rt[a[i]]=i; //右边第一个a[i]更新为在i上
	}
	memset(dp,-1,sizeof dp); //把dp数组赋值为-1,方便记忆化
	dfs(1,n,0);
	printf("%d:%d",dp[1][n][0],dp[1][n][1]); //输出
	return 0;
}// \(^_^)/ 完结撒花!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值