华中农业大学第四届程序设计大赛网络同步赛

B.

每组数据输入x,y,k,求只由x,y组成的k位数中能整除2^k的数的最小值是多少


解法:先证明一个结论:如果xy是一奇一偶,则一定且唯一存在这样的k位数。证明方法用数学归纳法:当k=1时,取偶数即可。设当k=m时已找出符合条件的m位数,由于m能被2^k整除,所以m除以2^(k+1)的余数只能是0或者2^k。现在来确定第m+1位数N(m+1)。它实际表示的值是Nm+1*10^m,显然这个数是能被2^m整除的。所以这个数除以2^(m+1)的余数也只能等于0或者2^m。取决于N(m+1)的奇偶性。N(m+1)是奇数则余数是2^m否则余数是0,所以如果构造出的m位数除以2^(m+1)余数是2^m,则N(m+1)取奇数,它在原来的m位数基础上对2^(m+1)的余数又加上2^m,刚好能被2^(m+1)整除。否则N(m+1)取偶数。这样就可以根据已经构造好的m位数来确定第m+1位数是多少。而且具有唯一性。如果xy给的是两个奇数显然没有解。如果是两个偶数,则先把他们同时除以2直到有一个数变为奇数。如果除以2后变成一奇一偶,因为两个数除了几次2(假设2次)那么实际上这样一奇一偶只需要被2^(k-2)整除。所以这时候只需要构造出后k-2位,前两位取两数中的较小者.

构造出第i位数后,维护当前的数对2^(i+1)2^k的余数。复杂度O(k^2)


D.

抢红包游戏,T组数据(T<=50),n个人进行抢红包的游戏,总共有m金额,每个人抢的红包的金额都是整数,每个人至少取一块钱的红包,而且如果一个人当前可以去1,2,3...x的数额的红包,那么抢得每一个红包的概率都是相同的,都是1/x,求第k个人取得的红包的期望。

N,M,K(1KN50, M500)

 

思路:

首先,题目有一个坑,如果当前取的人是最后一个,那么他所取得的红包的金额是固定的,我们用dp[i][j]表示前i个人取得金额为j的概率(i<n,i==n的时候特判),那么

dp[i][j]=dp[i-1][i-1]*(1.0/(n-i+1))+dp[i-1][i]/(n-i)+...

也就是我们可以用三层for去实现他,

第一层是第几个人,第二层是前面已经取了多少,第三层是这个人取多少


#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <stack>
#include <queue>
#include <set>
#include <bitset>
using namespace std;
 
const int MAXN = 55;
const int MAXM = 505;
int N, M, K;
double dp[MAXN][MAXM], all[MAXN][MAXM];
 
void input()
{
    scanf("%d%d%d", &N, &M, &K);
}
 
void solve()
{
    memset(dp, 0, sizeof(dp));
    memset(all, 0, sizeof(all));
    dp[0][0] = all[0][0] = 1;
    for(int i = 1; i <= N; i++)//有几个人
    {
        for(int k = i - 1; k <= M - N + i -1; k++)//前面已经取了多少个
        {
            if(i == N)
                dp[i][M - k] = all[i - 1][k];
            else
            {
                for(int j = 1; j <= M - N + i - k; j++) //这次去多少个
                {
                    double p = all[i - 1][k]/(M - N + i - k);
                    dp[i][j] += p;
                    all[i][j + k] += p;
                }
            }
        }
        if(i == K)
        {
            double ans = 0;
            for(int i = 1; i <= M - N + 1; i++)
                ans += i*dp[K][i];
            printf("%.6f\n", ans);
            return;
        }
    }
}
 
int main()
{
    //freopen("input.txt", "r", stdin);
   // freopen("output.txt", "w", stdout);
    int T;
    scanf("%d", &T);
    for(int t = 1; t <= T; t++)
    {
        input();
        solve();
    }
    return 0;
}

F

1015: LCS

Time Limit: 1 Sec  Memory Limit: 128 MB
[Submit][Status][Web Board]

Description

   Giving two strings consists of only lowercase letters, find the LCS(Longest Common Subsequence) whose all partition are not less than k in length.

Input

There are multiple test cases. In each test case, each of the first two lines is a string(length is less than 2100). The third line is a positive integer k. The input will end by EOF.

Output

    For each test case, output the length of the two strings’ LCS.

Sample Input

abxccdef
abcxcdef
3
abccdef
abcdef
3

Sample Output

4
6

给你两个字符串(长度小于等于2100),求两个字符串的LCS,这个LCS还有一些另外的要求,必须要是若干段连续的序列,而且每个段连续的序列的长度都必须大于等于k

 

思路:

如果我们普通的求解LCS的话,

If(s1[i]==s2[j])

dp[i][j]=dp[i-1][j-1]+1;

else

dp[i][j]=max(dp[i-1][j],dp[i][j-1]);

这个问题的话,我们只要再开一个数组C[i][j]表示第一个取i,第二个去j个时的最大值

判断dp[i][j]是不是大于等于k,(s1[i]!=s2[j]的时候,dp[i][j]=0)

如果大于等于k的话,c[i][j]=max(c[i-k][j-k]+k,c[i-k-1][j-k-1]+k-1,....c[i-dp[i][j]][j-dp[i][j]]+dp[i][j])

C[i-1][j-1]已经包含了后面的几项中的最大值,

所以如果dp[i][j]==k,C[i][j]=C[i-k][j-k]+k;

如果dp[i][j]>k,C[i][j]=max(C[i-k][j-k]+k,C[i-1][j-1]+1);

否则的话,C[i][j]=max(C[i-1][j],C[i][j-1])

时间复杂度O(n^2)


#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=2101;
char s1[maxn],s2[maxn];
int dp[maxn][maxn],C[maxn][maxn];
 
int main(){
    int k;
    memset(dp,0,sizeof(dp));
    memset(C,0,sizeof(C));
    while(scanf("%s%s",s1+1,s2+1)!=EOF){
        scanf("%d",&k);
        int len1=strlen(s1+1),len2=strlen(s2+1);
        dp[0][0]=0,C[0][1]=0,C[1][0]=0;
        int ans=0;
        for(int i=1;i<=len1;i++)
            for(int j=1;j<=len2;j++){
                if(s1[i]==s2[j])
                    dp[i][j]=dp[i-1][j-1]+1;
                else
                    dp[i][j]=0;
                if(dp[i][j]>=k){
                    C[i][j]=C[i-k][j-k]+k;
                    if(dp[i][j]>k)
                        C[i][j]=max(C[i][j],C[i-1][j-1]+1);
                }
                else
                    C[i][j]=max(C[i][j-1],C[i-1][j]);
                ans=max(C[i][j],ans);
            }
        printf("%d\n",ans);
    }
    return 0;
}

G.

给你一个n,m(n<=1000,m<=100), 接下来是na[i],然后nb[i],

要求:0CiAi(1in)

s的最小值

 

思路:

方法一:

因为比如第i个数已经取了x,将要取x+1,那么它的贡献为((x+1)*(x+1)-x*x)*b[i],

所以我们只要把每个数一个的贡献拆成ci,然后排一下序,去最小的m个便可以了。

 

方法二:

利用优先队列,取了这一个之后,再放一个这一个数的贡献进去(但是比赛中T)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值