算法

1、数轴上从左到右有n个点a[0],a[1]...a[n-1],给定一根长度为L的绳子,求绳子最多能覆盖其中的几个点。

简单意见的是枚举法,也就是以每个点为起点,遍历找最长,复杂度为O(n^2)

O(n)的解法:以每个i为起点,只希望覆盖更多的点。注意每次循环best和i都只增不减,尽管两个循环,复杂度还是O(n)的。

int calculate(int *a, int n, int L)
{
    int best = 0;
    for(int i=0; i + best < n; i++)
    {
        for( ; (i + best < n) && a[i + best] - a[i] <= L; best ++)
            ;
    } 
    return best;
}

2、子序列的个数(dp):给定一个正整数序列,序列中元素的个数和元素值大小都不超过105, 求其所有子序列的个数。注意相同的只算一次:例如 {1,2,1}有子序列{1} {2} {1,2} {1,1} {2,1}和{1,2,1}共6个。最后结果对10^9 + 7取余数。

解法:我们想想动态规划?严格来讲这个题不是一个dp的问题,因为它是一个数数(组合数学)的问题,不是一个优化的问题。我们来想想2^n个子序列是怎么来的——之前已经有2^(n-1)个了,再加上最后一个或者不加上最后一个,还有两种选择。

1、假设dp[i]表示前i项形成的子序列(含空)的个数。下标从1开始,初值是dp[0] = 1,对应代表空子序列。我们考虑第i项,如果所有的数都不相等(第i项的数没出现过),应该有dp[i] = dp[i – 1] * 2,其实就是考虑把第i个数放到最后或者不放到最后的情况。

2、然而,因为有可能有相同的数,我们假设第i个数出现之前最近的在j (j < i)位置也出现过,那么实际上我们这种简单*2会有重复,哪些子序列重复呢? 我们设数列为a, 并且下标从1开始。原来的前(j-1)项的子序列末尾添加上第j项和添加上第i项是一样的,就这些是重复的。所以dp[j-1]是重复的。此时 dp[i] = dp[i - 1] * 2 - dp[j - 1]

于是我们有了递推式:

dp[i] = dp[i – 1] * 2  如果a[i]不在之前出现

dp[i] = dp[i – 1] * 2 – dp[j – 1],如果a[i]最近在j的位置出现过。

这里有一个问题:我们如何找到a[i]最近在哪里出现呢?我们可以用一个hash,更简单的方式是直接把a[i]放到数组下标里,记录它最后一次出现的位置,因为数据范围并不大,可以承受。 所以我们有了一个时间复杂度O(n)的动态规划解法。

注意最后答案是dp[n] – 1 因为我们需要减掉一个全空子序列。

输入

   第1行:一个数N,表示序列的长度(1 <= N <= 100000)
   第2 - N + 1行:序列中的元素(1 <= a[i] <= 100000)

输出

   输出a的不同子序列的数量Mod 10^9 + 7。

输入示例

       4
       1
       2
       3
       2

输出示例
      13

#include<iostream>
#include<cstring>
#define mod 1000000007
using namespace std;


int a[100005];
int dp[100005];  //前i项的最大子序列个数
int s[100005];
 
int main()
{
    int n;
    cin >> n;
    memset(s,0,sizeof(s));
    for(int i=1; i<=n; i++)
        cin >> a[i];
    dp[0] = 1;  //空子序列
    for(int i=1; i<=n; i++)
    {
        dp[i] = (dp[i-1] * 2) % mod;
        if(s[a[i]] >0 )                      //a[i]和前面的一个数j重复,减去以j结尾的子序列个数
            dp[i]=(dp[i] - dp[s[a[i]]-1] + mod ) % mod; //前面(j – 1)个数的子序列最后跟上第j个数。
        s[a[i]] = i;
    }
    cout << (dp[n]-1) % mod << endl;  //减去空序列
    return 0;
}

3、Hash算法:

参考:https://blog.csdn.net/tanggao1314/article/details/51457585

除留余数法:理论研究表明,除留余数法的模p取不大于表长且最接近表长m素数时效果最好,且p最好取1.1n~1.7n之间的一个素数(n为存在的数据元素个数)


4、题目描述: 以P(w)表示词条w的概率, 假设已知P(南京)=0.8, P(市长)=0.6, P(江大桥)=0.4:P(南京市)= 0.3, P(长江大桥)=0.5:如果假设前后两个词的出现是独立的,那么分词结果就是( )

A. 南京市*长江*大桥

B. 南京*市长*江大桥

C. 南京市长*江大桥

D. 南京市*长江大桥

解:该题考察的是最大概率分词(划重点),其基本思想是:一个待切分的汉字串可能包含多种分词结果,将其中概率最大的作为该字串的分词结果。若某候选词在训练语料中未出现,其概率为0。

答题思路:A分词结果的概率为P(A)=P(南京市)*P(长江)*P(大桥),由于“长江”未在语料中出现,所以P(长江)=0,从而P(A)=0;
    同理可以算出B, C, D分词结果的概率分别是:
    P(B)=P(南京)*P(市长)*P(江大桥)=0.8*0.6*0.4=0.192;
    P(C)=P(南京市长)*P(江大桥)=0*0.4=0;
    P(D)=P(南京市)*P(长江大桥)=0.3*0.5=0.15。
    因为P(B)最大,所以为正确的分词结果。


5、题目描述:有一根27厘米的细木杆,在第3厘米、7厘米、11厘米、17厘米、23厘米这五个位置上各有一只蚂蚁。木杆很细,不能同时通过一只蚂蚁。开始时,蚂蚁的头朝左还是朝右是任意的,它们只会朝前走或调头,但不会后退。当任意两只蚂蚁碰头时,两只蚂蚁会同时调头朝反方向走。假设蚂蚁们每秒钟可以走一厘米的距离。编写程序,求所有蚂蚁都离开木杆的最小时间和最大时间。

1,求所有蚂蚁都离开木杆的最小时间和最大时间,即最后一只蚂蚁离开木杆的最小时间和最大时间。

2,两只蚂蚁相遇调头,由于蚂蚁的速度都是一样的,调头并没有改变最后一只蚂蚁离开木杆的时间。于是可以将“两只蚂蚁相遇掉头”看作“两只蚂蚁擦肩而过”,这样理解就更简单了。

3,所有蚂蚁离开木杆的最小时间就是所有(本题为5只)蚂蚁分别离开木杆的最小时间中的最大值;同理,所有蚂蚁离开木杆的最大时间就是对应的最大时间中的最大值。(最大值保证了最后一只蚂蚁离开杆)。

// 求最大时间
int GetMaxTime()
{
    int length = 27;                   //木杆的长度
    int speed = 1;                     //蚂蚁的爬行速度
    int pos[5] = { 3, 7, 11, 17, 23 }; //蚂蚁所在的位置
    int distance[5];                   //5只蚂蚁爬行对应的最大距离
    int maxLenth = 0;                  //5只蚂蚁爬行对应的最大距离中的最大值
    for (int i = 0; i < 5; i++)
    {
        //杆两端中最远的距离为蚂蚁爬行的最大距离
        if (pos[i] <= length / 2)
        {
            distance[i] = length - pos[i];
        }
        else
        {
            distance[i] = pos[i];
        }
        if (maxLenth < distance[i])
        {
            maxLenth = distance[i];
        }
    }
    return maxLenth / speed;
}

 

// 求最小时间
int GetMinTime()
{
    int length = 27;                        //木杆的长度
    int speed = 1;                          //蚂蚁的爬行速度
    int pos[5] = { 3, 7, 11, 17, 23 };      //蚂蚁所在的位置
    int distance[5];                        //5只蚂蚁爬行对应的最小距离
    int maxLenth = 0;                       //5只蚂蚁爬行对应的最小距离中的最大值
    for (int i = 0; i < 5; i++)
    {
        //杆两端中最近的距离为蚂蚁爬行的最小距离
        if (pos[i] <= length / 2)
        {
            distance[i] =  pos[i];
        }
        else
        {
            distance[i] =length- pos[i];
        }
        if (maxLenth < distance[i])
        {
            maxLenth = distance[i];
        }
    }
    return maxLenth / speed;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值