CodeCraft-21 and Codeforces Round #711 (Div. 2) A~C题解
原题链接
A. GCD Sum(思维+暴力)
解题思路
这道题我们直接遍历判断即可,为什么呢?我们总能确定在遍历的过程中不会遍历太久,因为如果对应的 n n n一直增 1 1 1,在这个过程中会存在 d i g i t _ s u m digit\_sum digit_sum和 n n n都是为偶数的。当然,这还是我们不考虑其他的情况下,因为我们要的只要满足 g c d gcd gcd大于 1 1 1,所以可以肯定的暴力即可。
AC代码
/**
*@filename:A
*@author: pursuit
*@CSDNBlog:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-03-30 14:42
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;
int t;
ll n;
ll gcd(ll n,ll m){
return n%m?gcd(m,n%m):m;
}
ll digit_sum(ll n){
ll ans=0;
while(n>0){
ans+=n%10;
n/=10;
}
return ans;
}
void solve(){
while(true){
if(gcd(n,digit_sum(n))>1){
cout<<n<<endl;
break;
}
n++;
}
}
int main() {
while(cin>>t){
while(t--){
cin>>n;
solve();
}
}
return 0;
}
B.Box Fitting(贪心)
解题思路
这道题就是纯粹的贪心,我们总想极大化的利用剩余空间,所以我们可以记录每一层的剩余空间,利用优先队列将这些存储起来,这样的好处就是我们每次都可以取到最大的剩余空间,以便我们知道还需不需要增加层数。 那么对应的方格,由于它们的高度就为 1 1 1,且由于方格不能进行旋转放置,所以我们只能水平放置,这样我们只关心它的长度,我们需要将它们的长度进行降序排列,然后我们直接遍历即可,如果空间不够,就加一层存储,所以最后优先队列的大小即是我们最小盒子的高度。
AC代码
/**
*@filename:B
*@author: pursuit
*@CSDNBlog:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-03-30 15:17
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;
int t,n,w;
bool cmp(int a,int b){
return a>b;
}
void solve(vector<int> &a){
sort(a.begin(),a.end(),cmp);
priority_queue<int> q;//优先队列,存放剩余空间。
q.push(w);//放入一块初始空间。
for(int i=0;i<n;i++){
//接下来开始判断剩余空间是否够用。
if(q.top()<a[i]){
q.push(w-a[i]);//不够用就要再加入一层。
}
else{
//够用就更新剩余空间大小。
int temp=q.top()-a[i];
q.pop();
q.push(temp);
}
}
cout<<q.size()<<endl;
}
int main() {
while(cin>>t){
while(t--){
cin>>n>>w;
vector<int> a(n);
for(int i=0;i<n;i++){
cin>>a[i];
}
solve(a);
}
}
return 0;
}
C. Planar Reflections(动态规划)
题意
由于这道题可能题意比较难理解,所以这里解释一下。题目中说明初始时有一个衰减年龄为
k
k
k的粒子,同时在它的前进方向有
n
n
n的平面,当粒子经过一个平面后,如果粒子的衰减年龄
k
k
k大于
1
1
1,那么它就会往相反的方向产生一个衰减年龄为
k
−
1
k-1
k−1的粒子。 问这样的一个粒子最终能够产生多少个粒子?
例如对于
n
=
2
,
k
=
3
n=2,k=3
n=2,k=3的时候,如下图:

我们发现这样的一个粒子最后能得到
4
4
4个粒子。
解题思路
我们还是继续分析这个样例,我们发现,这样拆分之后其实就像是一个状态被分解一样。我们可以定义这样的一个状态:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j],它表示运动前方还有k个平面,且衰变年龄为j的粒子所能产生的粒子数量。
那么样例中的
d
p
[
2
]
[
3
]
dp[2][3]
dp[2][3],它反弹后的粒子状态为
d
p
[
0
]
[
2
]
dp[0][2]
dp[0][2](由于反弹,方向改变,所以运动前方剩余平面为
n
−
i
n-i
n−i个),它继续往前的状态为
d
p
[
1
]
[
3
]
dp[1][3]
dp[1][3],即
d
p
[
2
]
[
3
]
=
d
p
[
n
−
i
]
[
3
−
1
]
+
d
p
[
2
−
1
]
[
3
]
dp[2][3]=dp[n-i][3-1]+dp[2-1][3]
dp[2][3]=dp[n−i][3−1]+dp[2−1][3]。
那么这就显而易见了,答案是
d
p
[
n
]
[
k
]
dp[n][k]
dp[n][k],到这一步我们最关键的就是将初始状态给找出来,对于衰变年龄为1的粒子,它不过经过多少个平面,所能产生的粒子就是它本身。而对于运动前方没有平面的粒子,是不能分裂的。粒子数量只有它本身。 即初始状态如下:
for(int i=1;i<=n;i++){
dp[i][1]=1;
}
for(int j=1;j<=k;j++){
dp[0][j]=1;
}
值得注意的一点就是在处理状态转移的时候,我们为了通过前面状态得到后面状态就必须先推出 j − 1 j-1 j−1的 1 1 1~ n n n所有情况,因为我们在 j j j的时候会引用到 n − i n-i n−i,这样就会造成错误。
AC代码
/**
*@filename:C
*@author: pursuit
*@CSDNBlog:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-03-30 15:37
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1000 + 5;
const int mod = 1e9+7;
int t,n,k;//n个平面,k表示为衰变年龄。
int dp[maxn][maxn];//定义状态:用dp[i][j]表示运动前方还有k个平面,且衰变年龄为j的粒子所能产生的粒子数量。
void solve(){
memset(dp,0,sizeof(dp));
//初始状态,对于衰变年龄为1的粒子,它不过经过多少个平面,所能产生的粒子就是它本身。
for(int i=1;i<=n;i++){
dp[i][1]=1;
}
//对于运动前方没有平面的粒子,是不能分裂的。粒子数量只有它本身。
for(int j=1;j<=k;j++){
dp[0][j]=1;
}
//为了通过前面状态得到后面状态,所以我们先要推出j的所有情况。
for(int j=1;j<=k;j++){
for(int i=1;i<=n;i++){
dp[i][j]=(dp[i-1][j]+dp[n-i][j-1])%mod;
}
}
cout<<dp[n][k]<<endl;
}
int main() {
while(cin>>t){
while(t--){
cin>>n>>k;
solve();
}
}
return 0;
}

浙公网安备 33010602011771号