K赌博老千——柯怡 (dp)

链接: https://www.nowcoder.com/acm/contest/121/K
来源:牛客网

题目描述

柯怡最近开始沉迷赌博,比如跟别人赌这次陕西皇家女子学院举办的校赛里面,AK人数的奇偶;

而这种赌博显然柯怡稳赚不赔,因为她可以偷偷参加校赛,然后在最后一分钟观察AK的人数,如果她赌的是奇数而那时有偶数个人AK,她只要用这剩下的一分钟自己AK一次就可以让数量变回偶数了;

聪明的ddjing发现了老千柯怡的阴险套路,万般无奈的柯怡只好更改赌博的套路,这次她觉得跟ddjing玩猜数字;

这个游戏很简单,规则是,柯怡和ddjing在游戏开始前,先规定数字的范围是1到n,而柯怡则从这n个数里面选出一个x并默默记在心中,每次ddjing去猜一个数y,柯怡会告诉ddjing,他是猜大了(y>x)还是猜小了(y<x),亦或者是猜对了(y==x);

而赌注则是,每次ddjing去猜一个数y的时候,若他没有猜中,那么他就需要向柯怡赠送y套女装(因为这是柯怡的最爱),如果他猜中了,那柯怡将会穿着小裙裙来到比赛现场给大家发气球;

虽然柯怡允许每次游戏的时候,ddjing可以猜任意次,直到猜中为止,但这显然可以为柯怡提供出千的可能,比如说,柯怡心中所选之数是x­1,而ddjing第一次就猜中了,然后柯怡就会马上变更她心中所选之数为x2,同时告诉ddjing他猜大了还是猜小了,但柯怡不会破坏游戏规则(比如说ddjing猜了5,柯怡告诉ddjing猜大了,那新的数就不能是小于等于5的数);

虽然ddjing知道这个游戏有很大的出千空间,但他知道,只要付出足够多的女装,就能稳定赢得最后的胜利;

但ddjing手头比较紧,他想知道,对于猜测1~n的游戏,在柯怡不断出千的情况下,他最少要准备多少套女装,才能保证一定能猜到最终结果;

输入描述:

第一行一个整数T(T<=100),代表数据组数;
对于每组数据,只有一行整数n(n<=300),代表游戏的数字范围;

输出描述:

对于每组数据,输出一个整数,代表ddjing至少需要准备的女装数目;
示例1

输入

3
1
2
3

输出

0
1
2

说明

当n=1时,ddjing只要猜1就能猜中,所以不用赠送女装;
当n=2时,ddjing柯怡猜1,下一次就肯定能猜中,所以只需要赠送一件女装就好;
当n=3时,ddjing只要猜2,下一次就肯定能猜中,只需要要赠送两件女装就好;

dp题目,因为这是猜数的最佳策略,所以并没有一个“所谓的最佳策略”,dp的过程就是把所有区间,把区间所有数字都猜一遍,取出最小值。

过程 : dp[i][j]表示区间[i,j]里面猜数字最小的衣服数,那么我们要把所有的区间怎么遍历?

for(i=1;i<=309;i++)

for(j=1;j<=309;j++)

dp[j][j+i]这里的j就是区间左端点,而i就是区间长度,每一个左端点+每一个区间长度,那么我们就遍历完所有所有的区间了。接下来,取值。

因为dp[j][j+i],所以区间为[j,j+i],那么k就从这里面取,当取了一个k,那么数字可以往左移和右移,所以衣服数=k+max(dp[j][k-1],dp[k+1][j+i]),这里max就是两边的最大值,因为是至少取的衣服数,所以最坏情况就是衣服数多的,接着去比较min(dp[j][j+i],k+max(dp[j][k-1],dp[k+1][j+i])),题目要求是最少要取的衣服数,所以这里要用min,也就是在这两个策略中,取衣服数最少的策略,所以转移方程就是dp[j][j+i]=min(dp[j][j+i],k+max(dp[j][k-1],dp[k+1][j+i]).

最后思考边界条件,边界就是k=j和k=j+i时,因为要求最小值,所以全部初始化应为INF,当k=j时,数字没办法左移,所dp[j][k-1]就是dp[j][j-1]=0,该点应该是0,而k=j+i时,dp[k+1][j+i]就是dp[j+i+1][j+i],这个同样数字没办法往右移,所以dp[j+i+1][j+i]=0,所以和前面没办法左移的情况时相同的,边界条件dp[i][i-1]=0;还有一种情况就是,如果区间变为一个点即[i,i]那么,这个点的取值也是0,因为只有一个数的时候,怎么猜都是对的,dp[i][i]=0.

综上,边界条件为dp[i][i-1]=0,dp[i][i]=0.

还有一点就是,留意dp过程,假设i=3,j=1,k=2,那么就是说dp[1][4]在区间[1,4]中,取第二个点,分成两个区间[1,1],[3,4]那么这两个区间一定是在之前就已经被遍历过的了(就是当i=1,j=3的时候dp[3][4]已经被遍历过的了),假设极端点k=1时,分成区间[1,0],[2,4](当i=2,j=2时dp[2][4]已经被遍历过),我们i从小到大,所以i=3时候i=2一定是被遍历过的了。所以其实也是符合dp思想,从局部最优解推到全局最优解的过程,最后要求的是区间[1,n]所以输出dp[1][n]。

通过这道题,我也加深了dp的思想,希望能独立做出更多的dp题目吧。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
int dp[400][400];
int main()
{
    int i,j,k,n;
    //dp[i][j]表示区间[i,j]
   memset(dp,999999,sizeof(dp));
    for(int i=1;i<304;i++)
    {
        dp[i][i]=0;
        dp[i][i-1]=0;
    }
    for(i=1;i<=309;i++)//其实i就是区间长度
    {
        for(j=1;j<=309;j++)//j就是区间左端点[j,j+i]即可表示一个区间
        {
            if(i+j>309) break;//不可能情况
            for(k=j;k<=i+j;k++)//在区间[j,j+i]中任选一个k值
            {
                dp[j][j+i]=min(dp[j][j+i],k+max(dp[j][k-1],dp[k+1][j+i]));
            }

        }//对于这里所有i,j都被遍历过,因此所有的区间都被遍历过了
    }
    int t;
   // for(i=0;i<=309;i++) printf("%d  %d\n",i,dp[1][i]);
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        printf("%d\n",dp[1][n]);
    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值