明天F补完题就加上()
放假了又能网瘾漏w
上周的abc太简单了这周的好难,E出了个数位dp想法以为是想复杂了结果还真是...
Tasks - AtCoder Beginner Contest 336
A - Long Loong
题意:
输出龙的拼音,把'o'换成连续的n个'o'
代码:
void solve()
{
int n;
scanf("%d", &n);
printf("L");
while (n--)printf("o");
printf("ng\n");
}
B - CTZ
题意:
输出n的二进制表示末尾有多少个连续的0
代码:
直接bitset偷懒了
void solve()
{
int x;
scanf("%d", &x);
bitset<40>st = x;
for (int i = 0;; ++i)
{
if (st[i] != 0)
{
printf("%d", i);
return;
}
}
}
C - Even Digits
题意:
输出仅由偶数构成的十进制数中第k小的数
题解:
看成五进制即可,记得特判n=1的情况
void solve()
{
LL n;
scanf("%lld", &n);
vector<int>ans;
--n;
if (n == 0)
{
printf("0");
return;
}
while (n)
{
ans.push_back(n % 5);
n /= 5;
}
while (ans.size())
{
printf("%d", ans.back() * 2);
ans.pop_back();
}
}
D - Pyramid
题意:
给出一个长度为n的正整数数组,你可以进行以下两种操作:
1:将一个元素-1
2:在数组开头或者末尾删除一个元素
使得数组最终变成1,2,3,...,k-1,k,k-1,...,3,2,1的形式,求可能的最大的k
题解:
把每个ai想象成高度,找最大的k相当于是在这个图里找一个最大的金字塔形的东西,例如样例1:
22311 121
#
### -> #
##### ###
l[i]表示点i在左斜边上时i位置的最大高度,转移为l[i] = min(l[i - 1] + 1, a[i]),r[i]同理,答案为max({ min(l[i], r[i]) }),具体做法见代码
int a[N], l[N], r[N];
void solve()
{
int n, ans = 0;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for (int i = 1, j = n; i <= n; ++i, --j)
{
l[i] = min(l[i - 1] + 1, a[i]);
r[j] = min(r[j + 1] + 1, a[j]);
}
for (int i = 1; i <= n; ++i)
ans = max(ans, min(l[i], r[i]));
printf("%d\n", ans);
}
E - Digit Sum Divisible
题意:
一个数的数位和为他的十进制表示上的所有数的和,当一个数能被他的数位和整除我们成这个数是好的数,求从0到n一共有多少个好的数
题解:
先枚举所有可能的数位和,对于每个数位和m我们做一个dp求数位和为m且能被m整除的小于等于n的数的数量:
设dp[i][j][k]为对于十进制数的前i位,数位和为j,该数当前的值取模m后的余数为k时的方案数,对于每个确定的dp[i][j][k],它转移到下一位时,设下一位填入的数为x,转移为:dp[i - 1][j + x][(k + x * pow(10, i - 1) % m] += dp[i][j][k],关于限制当前数小于等于n,我们只需要在求完每一位数之后把超过n的部分减去即可。大致思路是这样,具体做法见代码
题解中的数位和我在代码中用的是剩余的数位和,思路是一样的就是和题解中的转移j要反一下
LL pw[20], dp[16][140][140];
void solve()
{
LL n, ans = 0;
scanf("%lld", &n);
LL t = n, mx = 0;
vector<int>a;
while (t)
mx += t % 10, a.push_back(t % 10), t /= 10;
mx = max(mx, a.back() - 1 + ((LL)a.size() - 1) * 9);
pw[0] = 1;
for (int i = 1; i < 20; ++i)
pw[i] = pw[i - 1] * 10;
for (int m = 1; m <= mx; ++m)//枚举数位和m
{
LL s = m, md = 0;//按n的每一位选数时的数位和和余数
memset(dp, 0, sizeof dp);
dp[a.size()][m][0] = 1;
for (int i = a.size(); i > 0; --i)//第i位,准备填第i-1位
{
for (int j = 0; j <= m; ++j)//填数前数位和还剩j
{
for (int k = 0; k < m; ++k)//余数是k
{
for (int x = 0; x <= 9 && j - x >= 0; ++x)//第i-1位填x的转移
dp[i - 1][j - x][(k + x * pw[i - 1]) % m] += dp[i][j][k];
}
}
//减去超过上界的
for (int x = a[i - 1] + 1; x <= 9 && s - x >= 0; ++x)
{
int j = s, k = md;
dp[i - 1][j - x][(k + x * pw[i - 1]) % m] -= 1;
}
s -= a[i - 1], md = (md + a[i - 1] * pw[i - 1]) % m;
}
ans += dp[0][0][0];
}
printf("%lld\n", ans);
}
写的挺抽象的,应该还有更好的做法
F - Rotation Puzzle
题意:
给出一个n行m列的二维矩阵,其中的数字是一个排列(从1到n*m且两两不同),你可以在其中选择一个n-1行m-1列的子矩阵然后将它旋转180度,问最少多少步能将矩阵变成形如{1,2,3},{4,5,6},{7,8,9}的形式(看样例图能好理解),最多执行20步操作,20步内无法完成输出-1
题解:
题目限制了操作步数小于20,对于每个矩阵我们有四种变换方式,去掉往回走的操作直接暴搜复杂度是O(3^20*n*m)(n*m是变换、存储矩阵需要的常数),显然是不能接受的复杂度。此时我们可以双向广搜,显然旋转与旋转的逆操作时一样的,对终点和起点都搜至多10步,对起点和终点搜到的交集求一下它到起点、终点的最短路的和,取最小值就是最终答案。感觉挺典的,题目给了不超过20步好像有点太明显是暴搜优化了...
define真好用,这个代码我本地跑了感觉好慢交上去居然直结果了
typedef vector<vector<int>>grid;
#define makegrid(a) grid a(n,vector<int>(m))
int n, m;
map<grid, int>mpa, mpb;
grid fun(grid g, int ux, int uy)//旋转以ux,uy为左上角的大小为(n-1)*(m-1)的子矩阵
{
makegrid(res);
res = g;
for (int i = 0; i < n - 1; ++i)
{
for (int j = 0; j < m - 1; ++j)
{
int tx = n - 2 - i, ty = m - 2 - j;
res[ux + i][uy + j] = g[ux + tx][uy + ty];
}
}
return res;
}
void bfs(grid g, map<grid, int>&mp)
{
queue<pair<grid, int>>q;
q.push({ g ,0 });
mp[g] = 0;
while (q.size())
{
grid u = q.front().first;
int step = q.front().second;
q.pop();
if (step >= 10)return;
for (auto x : { 0,1 })
{
for (auto y : { 0,1 })
{
grid v = fun(u, x, y);
if (mp.find(v) == mp.end())
{
mp[v] = step + 1;
q.push({ v,step + 1 });
}
}
}
}
}
void solve()
{
scanf("%d%d", &n, &m);
makegrid(a);
makegrid(b);
for (int i = 0, tot = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
scanf("%d", &a[i][j]);
b[i][j] = ++tot;
}
}
bfs(a, mpa);
bfs(b, mpb);
int ans = INF;
for (auto& it : mpa)
{
if (mpb.find(it.first) != mpb.end())
ans = min(ans, it.second + mpb[it.first]);
}
if (ans == INF)printf("-1\n");
else printf("%d\n", ans);
}