算法总结系列——数组的巧妙运用+动态规划

前言

做完今天美团的912笔试编程题。感觉这个题目很巧,让我深刻领略了数组的巧妙运用。决定记录下,下次再遇到相关数组的巧妙运用时更新下。

正题

平时经常用数组来进行一些巧妙的事情,比如,用数组存储a-z所有字符在这个字符串中出现的次数。我们经常会把需要用到的值直接当做数组下标来用,这样就不需要再去遍历数组来得到想要的值。比如将要讲述的题目:

题目大意如下,有n个数(2<=n<=10^5),从中选取任意两个数,直接组合,求能组成7的倍数的个数。比如下面这组数据
3
127,1996,12
其中能组成7的倍数的,是127 12,1996 12,1996 127,12 1996。所以答案为4。

刚开始看这个题目的时候一头雾水,根本不知道如何下笔。两个for循环肯定不行,超时了。那么有没有巧妙的方法呢?

思路

仔细观察,发现两个数组合,假设a和b自由组合,那么就是a*(10k)+b或者 b*(10k)+a,其中,10k代表b的位数或a的位数。就可以以这个作为突破口,在刚输入a的时候,就将a左移0-9位的数全部存储起来(这里的座左移不是二进制的做移动,而是直接添0),下次运用时直接找有几个这样的值就行了。题目需要找到7的倍数的人,所以a移动后 mod 7的值要存储起来,以及a移动的位数肯定也要记录。
巧妙运用数组,数组的维数一定要选取好,这和动态规划一样,可以多点,但绝不能少,可以由多到少慢慢递减。直接使用某个值作为数组的下标,直接访问而不需要再循环去遍历查找。
那么需要记录什么呢?首先,肯定需要记录下a左移后mod 7的值(为了后续直接查找引用数组),这里作为数组第一维,还需要a左移的位数,这样才知道是不是刚好这两个数能自由组合,这是第二维。貌似二维就足够了,但是真的够了吗?
no,编程过程中,会发现,还需要第三维,这里第三维大小只需要申明为2就行,例如,dp[i][j][0],代表没有移动(0)的时候,位数为j的人,在mod 7为i的数有几个。dp[i][j][1],代表像左移动了 j位之后,mod 7 ==i 的数有几个。
核心代码如下:

struct Node{
	int x;//x代表值
	int y;//y代表位数。
}node[MAXN];
long long multi[10];

void handle(){
	memset(dp,0,sizeof(dp));
    multi[0]=1;
    for(int i=1; i<10; i++)
    {
        multi[i] = multi[i-1]*10;
    }
    for(int i=0; i<n; i++)
    {
        int temp;
        scanf("%d",&temp);
        node[i].x = (long long)temp;
        node[i].y = 0;
        while(temp)//求出temp的位数存储在node[i].y中。
        {
            node[i].y++;
            temp = temp/10;
        }
        dp[(int)node[i].x%7][node[i].y][0]++;//这里0采用node[i].y来存储,就是为了之后把该值作为组合的右边值时方便直接调用。
        for(int j=1; j<10; j++)
        {
            int mod = (int)((long long)node[i].x)*multi[j]%7;
            dp[mod][j][1]++;//这里1时,采用左移了的j位来存储而不是采用node[i].y也是为了之后吧该值作为组合的左边值时方便直接调用。
        }
    }
    int ans = 0;
    for(int i=0; i<n; i++)
    {
        int mod = (int)node[i].x%7;
        ans += dp[(7-mod)%7][node[i].y][1];//这里%7也别忘了,比如mod为0时,(7-mod)%7应该对应0。
        if((node[i].x*multi[node[i].y]%7)==((7-mod)%7))//扣除自己跟自己组合。这里容易遗漏,比如127,127127也是7的倍数。LZ在这里纠结了好久。
            ans--;
        for(int j=1; j<10; j++)
        {
            mod = (int)((long long)node[i].x)*multi[j]%7;
            ans+=dp[(7-mod)%7][j][0];
        }
    }
    printf("%d\n",ans/2);
} 

总结

总结一下,这个题目就是采用数组来记录,因为左右移动的位置不大,而要求为7的倍数又可以采用mod来做,范围都很小,所以可以采用数组来记录下。这个题目运用确实很巧,平时在项目中一般会采用hashmap嵌套hashmap或嵌套arraryList的方式,这里当然也可以,但是显然数组更让人喜欢一点点(因为算法题目)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章