Topcoder:SRM 708 算法题解

题目翻译

250分题目:SafeBetting

赌徒有b块钱,他想把自己手上的钱增加到c块,同时他又不想输的太惨,因此必须保证每次下注后手上不少于a块钱。每次下注,赢了则下注的钱按双倍奉还(收益率100%),输了则分文不剩(收益率-100%)。问赌徒至少需要下注几次才能达到目标。

500分题目:BuildingStrings

定义一个字符串的得分score为:字符串长度length与字符串中不同的字母的个数unique_num的乘积。给定一个总分K,求一系列字符串使得他们的得分的总和为K。要求每个字符串由小写英文字母组成,长度不超过50。当有多个答案时,任意输出一个即可。

1000分题目:PalindromicSubseq2

给定一个字符串S,其中的每个字母的得分X[i]由它左右两边的对称字符串的个数来决定(即左右两边的字符串s1=S[:i],s2=S[i+1:], 从s1中取子字符串s11,从s2中取子字符串s22,s11与s22排列相反)。定义Y[i] = ((i+1) * X[i]) modulo 1,000,000,007,求Y[i]按位异或后的值。

解题思路及代码

250分题解:SafeBetting

赌徒每次可以赚的钱的数目为手上的钱减去最低限度。设可用资金为M=b-a,可用目标资金 N=c-a, 则每次赌博可用资金增加一倍,输出为log(M/N,2)向上取整即可。

import math,string,itertools,fractions,heapq,collections,re,array,bisect
class SafeBetting:
    def minRounds(self, a, b, c):
        return math.ceil(math.log(float(c-a)/(b-a),2))
500分题解:BuildingStrings

这是一个构造问题,只需要进行构造即可。先构造几个基础字符串,再将得分分解为这些基础字符串的和即可。每个字符串最长为50,最多有26个独特字母,最高得分为50×26=1300分。因此构造的时候,可以构造三种字符串:1300分的字符串,50×m(m<26)分的字符串,n(n<50)分的字符串。将每中得分进行分解即可。

class BuildingStrings:
    def findAny(self, K):
        S_26='abcdefghijklmnopqrstuvwxyz'
        S_1300='abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx'
        N_1300=K/1300
        K=K-N_1300*1300
        N_50=K/50
        K=K-N_50*50
        N_1=K
        ret=[]
        ret+=[S_1300 for i in range(N_1300)]
        if N_50>0:
            S_50k=S_26[:N_50]+'a'*(50-N_50)
            ret+=[S_50k]
        #print ret
        S_1='a'*N_1
        ret+=[S_1]
        #print ret
        return tuple(ret)
1000分题解:PalindromicSubseq2

这个问题的主要难度在于求字符串S每个位置上的得分X[i],如果针对每个位置都进行一遍计算的话,在复杂情况下肯定会超时。容易联想到最长公共子序列问题中的动态规划算法(leetcode上有公共子序列个数的问题,题名Distinct Subsequences)。

如果之前求解过公共子序列个数的问题的话,这个问题可以看作公共子序列个数问题的改进版。
首先,回顾公共子序列个数问题:给定字符串S和T,求S和T中公共子序列的个数。利用动态规划来求解,S中前i个字母的子字符串S1,和T中前j个字母的字符串T1,dp[i][j]表示S1和T1中公共子序列的个数。

转移方程:dp[i][j]包含四个部分,公共子序列中同时包含S[i]和T[j],公共子序列中不含S[i]和T[j],公共子序列中包含S[i]不包含T[j],公共子序列中不包含S[i]包含T[j]。
Topcoder:SRM 708 算法题解_第1张图片
则如果S[i]与T[j]相同,则dp[i][j]=dp[i-1][j]+dp[i][j-1]
如果S[i]与T[j]不相同,则dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]
依次计算dp即可。

接着,再将现在的问题转化为公共子序列的个数的问题。令S=S,T=S_reverse(S_reverse为S的逆序字符串),则X[i]=dp[i][n-i+1]。然后就是直接求解了。

class PalindromicSubseq2:
    def dynamicplan(self,s):
        N=len(s)
        #dp={j:{i:0 for i in range(N) if i>j} for j in range(N)}
        dp=[[0 for i in range(N)] for j in range(N)]
        for i in range(N):
            dp[i][N-1]=1
        for j in range(N):
            dp[0][j]=1
        for i in range(1,N):
            for j in range(N-2,i-1,-1):
                if s[i-1]==s[j+1]:
                    dp[i][j]=dp[i-1][j]+dp[i][j+1]                  
                else:
                    dp[i][j]=dp[i-1][j]+dp[i][j+1]-dp[i-1][j+1]
        return dp
    def solve(self, s):
        N=len(s)
        dp=self.dynamicplan(s)
        count=0
        for i in range(N):
            X_i=dp[i][i]
            #print X_i,
            count_i=(i+1)*X_i%1000000007
            count^=count_i
        return count

思路是对的,但是python的运行速度实在太慢,能通过简单的测试,但是系统测试的时候有一个测试案例没有通过,程序运行了2.493s,导致超时了。看来遇到对运行速度有要求的题目,还是得拿C/C++来写。

你可能感兴趣的:(Python-学习,算法学习)