B.
每组数据输入x,y,k,求只由x,y组成的k位数中能整除2^k的数的最小值是多少
解法:先证明一个结论:如果x和y是一奇一偶,则一定且唯一存在这样的k位数。证明方法用数学归纳法:当k=1时,取偶数即可。设当k=m时已找出符合条件的m位数,由于m能被2^k整除,所以m除以2^(k+1)的余数只能是0或者2^k。现在来确定第m+1位数N(m+1)。它实际表示的值是N(m+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位数是多少。而且具有唯一性。如果x和y给的是两个奇数显然没有解。如果是两个偶数,则先把他们同时除以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(1≤K≤N≤50, M≤500)
思路:
首先,题目有一个坑,如果当前取的人是最后一个,那么他所取得的红包的金额是固定的,我们用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), 接下来是n个a[i],然后n个b[i],
要求:0≤Ci≤Ai(1≤i≤n)
求s的最小值
思路:
方法一:
因为比如第i个数已经取了x次,将要取x+1次,那么它的贡献为((x+1)*(x+1)-x*x)*b[i],
所以我们只要把每个数一个的贡献拆成ci份,然后排一下序,去最小的m个便可以了。
方法二:
利用优先队列,取了这一个之后,再放一个这一个数的贡献进去(但是比赛中T了)