题解——碰杯

题目描述

今天的酒席有n个人,他们要同时举杯,成对碰杯。碰杯的时候,不能有人不参与碰杯,也不希望有手臂交叉这种别扭的情况出现每个人都有一个喜爱的酒种类,每个人想要与和自己喝一样酒的人碰杯,请你设计一个方法,在保证每个人参与碰杯,且没有手臂交叉的情况下,有最多的人与喝一样酒的人碰杯,输出最多有多少人能与喝一样酒的人碰杯。

输入输出

第一行一个数n,表示酒桌上人的个数。
第二行n个数ci,逆时针方向依次表示坐在第i个位置的人喝哪种酒,第1个和第n个人是相邻的。

样例

【输入样例1】
6
1 2 2 1 3 3
【输出样例1】
3

对于这道题目,显然可以看出是一道区间Dp,但是如何进行合法的区间合并,是本题的关键。
没有手臂交叉,就是在进行区间合并时不能将合法的分割点所分成的左右区间中进行统计匹配。那么,这就需要我们合并区间时做一些修改。
我们来分析下面这组数据:
现定义2个合法区间 dp[i~k-1] 和 dp[k+1~j-1]。如果分割点为 K ,末端点为 j , 我们保证 dp[i~k] 和 dp[k~j] 中没有人交叉碰杯,那么只需要将 k , j 提出在进行合并即可。 该方法正确性显然。

所以我们得到转移方程

  ( c[ j ] == c[ k ] )dp[ i ][ j ] = max( dp[ i ][ j ] , dp[ i ][ k - 1 ] + dp[ k+1 ][ j - 1 ] + 1 );
  ( c[ j ] != c[ k ] )dp[ i ][ j ] = max( dp[ i ][ j ] , dp[ i ][ k - 1 ] + dp[ k+1 ][ j - 1 ] );

那么,本题就差一个区间Dp的框架了。

AC代码

注:本题可以不开 long long

#include<bits/stdc++.h>
using namespace std;
#define  ll long long 
ll dp[ 1005 ][ 1005 ] , ans , N , c[ 1005 ] ;
inline ll read()
{
    ll s = 0,w = 1;
    char g = getchar();
    while(g<'0'||g>'9'){if(g=='-')w*=-1;g = getchar();}
    while(g>='0'&&g<='9'){s = s*10+g-'0';g = getchar();}
    return s*w;
}
void  init_(){
    freopen("toasting.in","r",stdin);
    freopen("toasting.out","w",stdout);
}
void  prepare_(){
    N = read() ;  memset( dp , 0 , sizeof(dp) ) ;
    for( register int  i = 1 ; i <= N ; i ++ )c[ i ]  = read() ; 
}
void  Dp_(){
    for( register int  j = 2 ; j <= N ; j++ )
        for( register int i = j - 1 ; i >= 1 ; i -= 2 )
            for( register int k = i ; k <= j - 1 ; k += 2 ){ // compared c[ i ] with c[ k ] , so we need add dp i,k-1  k+1,j-1 
                if( c[ j ] == c[ k ] )dp[ i ][ j ] = max( dp[ i ][ j ] , dp[ i ][ k - 1 ] + dp[ k+1 ][ j - 1 ] + 1 );
                else  dp[ i ][ j ] = max( dp[ i ][ j ] , dp[ i ][ k - 1 ] + dp[ k+1 ][ j - 1 ] );
        }
}
void  Find_ans(){
    cout<< dp[ 1 ][ N ] <<endl;
}
int  main()
{
    init_();
    prepare_() ;
    Dp_() ;
    Find_ans();
    return  0 ;
}
相信下一次有人看这篇博客也是我退役的时候了。
有疑惑和建议,可以留下评论或私我。
如果你喜欢我的文章,请点赞支持,谢谢。

转载于:https://www.cnblogs.com/ssw02/p/10848498.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值