keep going!!! CF 283D Cows and Cool Sequences

http://www.codeforces.com/problemset/problem/283/D

Cows and Cool Sequences

这个题挺好的,纠结了好久,看了几个人的报告,终于理解了,但还是wa4次。梳理一下思路。

给一个字符串,a1 a1 a3 a4 ......

要是an 可以用a(n+1)连续个数的和表示,问最少需要改变多少个数字。

这个题的收获是:

1:遇到这种问要改变多少个能达到某种状态的,应该多数就是DP了,看看某一位能由哪些状态转换过来

2:两个变量之间的关系不好表达的时候,要用数学手段转换成有限中情况。

针对这个题,如何DP? 我们设pd[i]表示从第0个数字到第i个数字这一串是一个cool的串,并且第i个数字没有变化,所需要的最小变化量。我们的串从0开始到n-1。在原串的最后加一个1,即第n-1个数字可以由1个连续的数字的和表示,显然就是他自己。那么dp[n]就是第n个数字之前的串已经很cool了,而第n个数字没有变化需要的最少变化。

状态如何转移?

假设现在要求dp[i],那么它的值可以由dp[0],dp[1],.......dp[i-2],dp[i-1]得来。假设是从dp[j]转化来的,也就是把ij之间的i-j-1个数变了,使这一串都是cool的,那么dp[i]=min(dp[i],dp[j]+i-j-1)

但是,并不是所有的dp[j]都能转化到dp[i],我们需要推导他们的关系。

假设x能由y个连续的数字之和表示,则s是任意数。

y是奇数的时候,明显x只要是y的任意倍就可以表示成y个连续数的和。

y是偶数的时候,2xy的奇数倍,这个不太好看。我们知道任意的数都可以表示成某些质数的幂的和,那么2xy的奇数倍,也就是x分解后比y分解后的2正好少一个,并且剩余部分是正整数倍(比如是奇数)。我们把每个数分成两部分,B是它约数2的个数,L存放剩下的部分,则x能由y个连续的数字之和表示可以看作:

y是奇数的时候,x.L%y.L==0

y是偶数的时候,x.L%y.L==0&&x.B+1==y.B

以上是两个相邻的数的关系,而从dp[j]转化到dp[i]很简单,依然假设是xy,但是他们不一定相邻,如果x.L%y.L==0肯定不行,如果x.L%y.L==0,那么看y,如果y是奇数,即y.B==0,那么可以,比如x14y7,那么可以是14 14 777,很随意了。如果y是偶数,即y.B不是0,那么前一项的B一定是y.B-1,如果xy下标相差和B相差相同,则可以转换,比如24816,如果y.B比两者的下标差要小的话(这这里wa3次。另一次是万恶的long long),那么就无压力转换了,比如105551020,从205的时候B值降为0,但是这是这个数就是奇数了,前面的数又是它的倍数,随便填都可以了。

问题解决,dp即可,可能没太说明白,要反复琢磨,反复看,我也是看别人的看明白的。

贴代码:

#include "iostream"
#include "cstdio"
#include "cstring"
#include "cmath"
#include "stdlib.h"
#include "algorithm"
using namespace std;

#define MAXN 5010
long long str[MAXN];
int B[MAXN],dp[MAXN];
int n;

bool ok(int j,int i)
{
    if(str[j]%str[i]!=0)
        return false;
    if(B[i]==0)
        return true;
    else
    {
        if(i-j==B[i]-B[j])
            return true;
        if(B[i]<i-j)
            return true;
    }
    return false;
}

int main()
{
    scanf("%d",&n);
    memset(B,0,sizeof(B));
    for(int i=0; i<n; i++)
    {
        scanf("%I64d",&str[i]);
        while(str[i]%2==0)
        {
            B[i]++;
            str[i]/=2;
        }
    }
    str[n]=1;
    dp[0]=0;
    for(int i=1; i<=n; i++)
    {
        dp[i]=i;
        for(int j=0; j<i; j++)
            if(ok(j,i))
                dp[i]=min(dp[i],dp[j]+i-j-1);
    }
    printf("%d\n",dp[n]);
    return 0;
}
水平有限,众神轻喷。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
本火锅店点餐系统采用Java语言和Vue技术,框架采用SSM,搭配Mysql数据库,运行在Idea里,采用小程序模式。本火锅店点餐系统提供管理员、用户两种角色的服务。总的功能包括菜品的查询、菜品的购买、餐桌预定和订单管理。本系统可以帮助管理员更新菜品信息和管理订单信息,帮助用户实现在线的点餐方式,并可以实现餐桌预定。本系统采用成熟技术开发可以完成点餐管理的相关工作。 本系统的功能围绕用户、管理员两种权限设计。根据不同权限的不同需求设计出更符合用户要求的功能。本系统中管理员主要负责审核管理用户,发布分享新的菜品,审核用户的订餐信息和餐桌预定信息等,用户可以对需要的菜品进行购买、预定餐桌等。用户可以管理个人资料、查询菜品、在线点餐和预定餐桌、管理订单等,用户的个人资料是由管理员添加用户资料时产生,用户的订单内容由用户在购买菜品时产生,用户预定信息由用户在预定餐桌操作时产生。 本系统的功能设计为管理员、用户两部分。管理员为菜品管理、菜品分类管理、用户管理、订单管理等,用户的功能为查询菜品,在线点餐、预定餐桌、管理个人信息等。 管理员负责用户信息的删除和管理,用户的姓名和手机号都可以由管理员在此功能里看到。管理员可以对菜品的信息进行管理、审核。本功能可以实现菜品的定时更新和审核管理。本功能包括查询餐桌,也可以发布新的餐桌信息。管理员可以查询已预定的餐桌,并进行审核。管理员可以管理公告和系统的轮播图,可以安排活动。管理员可以对个人的资料进行修改和管理,管理员还可以在本功能里修改密码。管理员可以查询用户的订单,并完成菜品的安排。 当用户登录进系统后可以修改自己的资料,可以使自己信息的保持正确性。还可以修改密码。用户可以浏览所有的菜品,可以查看详细的菜品内容,也可以进行菜品的点餐。在本功能里用户可以进行点餐。用户可以浏览没有预定出去的餐桌,选择合适的餐桌可以进行预定。用户可以管理购物车里的菜品。用户可以管理自己的订单,在订单管理界面里也可以进行查询操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值