题号 | 题目 |
---|---|
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
p−x 或
x
−
p
x−p
x−p 的位置。
发现只有当
x
x
x 恰好等于
p
p
p 的时候才可以结束,否则
p
p
p 将变为依然在
[
1
,
d
)
[1,d)
[1,d) 中的另一个位置
p
p
p′。那么其实等价于在
1
∼
d
−
1
1∼d−1
1∼d−1 中一直随机一个数,直到随机到
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=d−1d−2(s+1)+d−11
解方程得到
s
=
d
−
1
s=d−1
s=d−1。
那么当棋子在
[
1
,
d
)
[1,d)
[1,d) 时,期望
d
−
1
d−1
d−1 次才会回到点 0。
那么当棋子所在位置
p
≥
d
p≥d
p≥d 时,由于只会不停往左走,所以可以设
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=i−d+1∑i−1f[j]+1)+f[i−d]/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;
}