牛客模拟赛5【普及组】

题号题目
T1购物
T2交换
T3最少移动
T4飞行棋
得分280/400

T1

思路

直接计算

代码

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long T,n,x,k,js1,js2,ans;
int main()
{
    cin>>T;
    while(T--)
     {
     	js1=0,js2=0,ans=0;
     	scanf("%d%d%d",&n,&k,&x);
     	js1=n/(k+1);
     	js2=n%(k+1);
     	ans=ans+js1*k*x+js2*x;
     	cout<<ans<<endl;
	 }
	return 0;
}

T2

思路

发现此题只有串头和串尾会对答案有贡献。
直接把串复制一遍,然后 O ( n ) O(n) O(n) 扫描找最大.
并对串长取 min ⁡ \min min

代码

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int ans,maxn;
string s;
int main()
{
    cin>>s;
    int n=s.size();
    s=s+s;
    for(int i=0; i<=s.size()-1; i++)
     {
     	if(s[i]=='1')
     	  maxn++;
     	else
     	  maxn=0;
     	ans=max(ans,maxn);
	 }
	cout<<min(ans,n);
	return 0;
}

T3

思路

此题如果总和均值是小数,就代表一定不可能实现,输出 − 1 -1 1.
对于次数直接扫一遍数组累计即可。

代码

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long T,n,js,ans;
long long a[1000010];
int main()
{
    cin>>T;
    while(T--)
     {
     	js=0;
     	cin>>n;
     	for(int i=1; i<=n; i++)
     	 {
     	 	scanf("%d",&a[i]);
     	 	js+=a[i];
		 }
		if(js%n!=0)
		 {
		 	cout<<-1<<endl;
		 	continue;
		 }
		ans=0;
		int z=js/n;
		for(int i=1; i<=n; i++)
		 {
		 	if(a[i]<z)
		 	  {
		 	 	a[i+1]=a[i+1]-(z-a[i]);
		 	 	ans=ans+(z-a[i]);
			  }
			if(a[i]>z)
			 {
			 	a[i+1]=a[i+1]+(a[i]-z);
		 	 	ans=ans+(a[i]-z);
			 }
		 }
	    cout<<ans<<endl;
	 }
	return 0;
}

显然要棋子在 [ 1 , d ) [1,d) [1,d) [ d , n ] [d,n] [d,n] 的时候分开做。
当棋子在 [ 1 , d ) [1,d) [1,d) 的时候,假设现在在位置 p p p,骰子扔到的是 x x x 点,那么棋子可能会变到 p − x p−x px x − p x−p xp 的位置
发现只有 x x x 恰好等于 p p p 的时候才可以结束,否则 p p p 将变为依然在 [ 1 , d ) [1,d) [1,d) 中的另一个位置 p p p′。那么其实等价于 1 ∼ d − 1 1∼d−1 1d1 中一直随机一个数,直到随机到 1 1 1 时停止,求期望次数
设期望 s s s 次扔到点 1 1 1,那么有

s = d − 2 d − 1 ( s + 1 ) + 1 d − 1 s=\frac{d-2}{d-1}(s+1)+\frac{1}{d-1} s=d1d2(s+1)+d11
解方程得到 s = d − 1 s=d−1 s=d1
那么当棋子在 [ 1 , d ) [1,d) [1,d) 时,期望 d − 1 d−1 d1 次才会回到点 0
那么当棋子所在位置 p ≥ d p≥d pd 时,由于只会不停往左走,所以可以 f [ i ] f[i] f[i] 表示点 i i i 开始期望多少次可以回到点 0 0 0,这样是没有后效性的
得到DP转移方程:

f [ i ] = ( ∑ j = i − d + 1 i − 1 f [ j ] + 1 ) + f [ i − d ] / d f[i] = (\sum_{j=i-d+1}^{i-1}{f[j]+1})+f[i-d]/d f[i]=(j=id+1i1f[j]+1)+f[id]/d
前缀和优化求和即可
时间复杂度 O ( T n ) O(Tn) O(Tn)

以上的部分解释载自zycdalao的博客

代码

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
double sum[1000010],f[1000010];
int T,n,d;
int main()
{
    cin>>T;
    while(T--)
     {
     	scanf("%d%d",&n,&d);
     	f[0]=sum[0]=1;
     	for(int i=1; i<d; i++)
     	   f[i]=d-1,sum[i]=sum[i-1]+f[i];
     	for(int i=d; i<=n; i++)
     	 {
     	 	f[i]=(sum[i-1]-sum[i-d]+d-1)/d+f[i-d]/d; //前缀和优化
     	 	sum[i]=sum[i-1]+f[i];
		 }
		printf("%.2lf\n",f[n]);
	 }
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值