AtCoder Beginner Contest 336
ABC336 总结
A - Long Loong
翻译
给定一个数 \(n\),请输出一个由一个 L
、\(n\) 个 o
、一个 n
和一个 g
组成的字符串(区分大小写)。
分析
按题意模拟即可。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n;
int main ()
{
cin>>n;
cout<<"L";
for(int i=1;i<=n;i++) cout<<"o";
cout<<"ng"<<"\n";
return 0;
}
B - CTZ
翻译
求一个正整数 \(N\) 二进制下末尾 \(0\) 的个数,\(1\le N\le 10^9\)。
分析
直接 lowbit
操作,然后取 \(2\) 的对数。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
int main ()
{
cin>>n;
m=n&(-n);
cout<<log2(m)<<"\n";
return 0;
}
C - Even Digits
翻译
定义满足没有前导零,且十进制下每个数位都为偶数的非负整数为好数。
求第 \(N\) 小的好数。\(1\le N\le 10^{12}\)。
分析
每个数位都是偶数,则每个数位都有五种情况,相当于把 \(n\) 转化为 \(5\) 进制数,再按照十进制来乘 \(2\)。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
ll n,m,t,a;
int main ()
{
cin>>n;
n--;
while(n)
{
m=n%5;
n/=5;
a+=m*t;
t*=10;
}
cout<<a*2<<"\n";
return 0;
}
D - Pyramid
翻译
对于正整数 \(k\),一个大小为 \(k\) 的“金字塔数列”为一个长度为 \(2k-1\) 的数列,里面的数字依次为 \(1,2,3,\dots k-1,k,k-1,\dots 3,2,1\)。
现在给一个长度为 \(n\) 的数列 \(S\),你可以进行以下操作任意次,使得数列最后变为一个“金字塔数列”:
- 选择一个数 \(i(1 \le i \le n)\),把 \(S_i\) 减少 \(1\)。
- 删除整个数列的第一个或最后一个数字。
问最后生成的“金字塔数列”的最大的 \(k\) 是多少。
分析
\(k\) 大小的“金字塔数列”,前 \(k\) 项单调递增,后 \(k\) 项单调递减,可以分开考虑,用线性 \(dp\)。\(dp[i][0]\) 表示以 \(a_i\) 为末尾的,经过多次操作可以成为金字塔数列前 \(k\) 项的序列的最大长度,\(dp[i][1]\) 表示以 \(a_i\)为首的,经过多次操作可以成为金字塔数列后 \(k\) 项的最大长度,最终合并计算答案即可。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,m,t,a[N],dp[N][3],ans;
int main ()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) dp[i][0]=min(dp[i-1][0]+1,a[i]);
for(int i=n;i>=1;i--) dp[i][1]=min(dp[i+1][1]+1,a[i]);
for(int i=1;i<=n;i++) ans=max(ans,min(dp[i][0],dp[i][1]));
cout<<ans<<"\n";
return 0;
}
F - Rotation Puzzle
翻译
gsczl71 有一个 \(H\) 行,\(W\) 列的二维数组。
每一次选择一个 \(H-1\) 行,\(W-1\) 列的连续矩阵,将其旋转 \(180\) 度。
目标:对于所有 \(1 \le i \le H,1 \le j \le W\),使得单元格 \((i,j)\) 中包含整数 \(\left(\left(i-1\right) \times W+j\right)\)。
你有最多 \(20\) 次操作机会,问你最少操作多少次?
若不可以达到要求,输出 -1
。
分析
翻转只有四种情况,最大操作次数为 \(20\),直接搜索状态数可达 \(4^{20}=1099511627776\),直接爆掉,但如果用双向搜索(meet in the middle),每个方向最大深度为 \(10\),这样状态数则为 \(2 \times 4^{10}=2097152\),轻松通过。
初状态与末状态等价,两者相遇可用特殊的值来判断,比如初状态为 \(1\),末状态为 \(2\),那么判断相遇就是值为 \(3\) 的时候。初末状态等价,可以只用一个队列,翻转操作需要略加思考,容易出错。可以用 string
和 map
实现 hash,将二维数组转化为字符串和字符串还原为数组与八数码相同。
注意:原数组出现相同的数则一定无法实现,输出 -1
,初末状态相同就不需要操作,输出 0
。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=10;
int m,n,a[N][N],b[N][N],tong[N*N],f[5][3]={{1,1},{0,1},{1,0},{0,0}},c[N][N];
string st,ed;
map<string,int> vis,d,v;
string reverse(int x,int y)
{
string s;
for(int i=1;i<n;i++)
for(int j=1;j<m;j++)
c[i+x][j+y]=b[n-i+x][m-j+y];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
s+=c[i][j]+'0';
return s;
}
void restore(string s)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
c[i][j]=b[i][j]=s[(i-1)*m+j-1]-'0';
}
int bfs(string x,string y)
{
queue<string> q;
q.push(x);q.push(y);
vis[x]=1;d[x]=0;
vis[y]=2;d[y]=0;
while(q.size())
{
string t=q.front();q.pop();
for(int i=0;i<4;i++)
{
restore(t);
int l=f[i][0],r=f[i][1];
string s=reverse(l,r);
if(vis[s]==vis[t]) continue;
if(vis[s]+vis[t]==3) return d[s]+1+d[t];
d[s]=d[t]+1;
if(d[s]>10) return 21;
vis[s]=vis[t];
q.push(s);
}
}
return 21;
}
int main ()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
st+=a[i][j]+'0';
tong[a[i][j]]++;
}
for(int i=1;i<=n*m;i++)
{
if(tong[i]!=1)
{
cout<<-1<<"\n";
return 0;
}
ed+=i+'0';
}
if(ed==st)
{
cout<<0<<"\n";
return 0;
}
int ans=bfs(st,ed);
if(ans>20) cout<<-1<<"\n";
else cout<<ans<<"\n";
return 0;
}