5.18周赛
反思一下,A题要不是第三个样例可能就WA不知道多少分了,即使有第三个样例step2 - (x + y)) % 2
要等于0我也推了好久(差点没整出来)
然后一直在B,C之间犹豫,想一会儿C想一会儿B的,结果差点两道题都没有做出来
B题出了好几个智障错误:
从1开始A,B一起枚举结果忘了i - A或者i - B可能(其实是一定)会小于0然后出现未知错误,搞了好久分开枚举才过了样例,这么点小事还是考试之后Phenning巨佬告诉我的
枚举水的时候误以为只要是一次性的背包就都从大到小枚举,忽略了一次性背包问题枚举的本质(不能让一个东西被反复更新)因为/2是一个变小的行为,所以需要从小到大枚举才不会导致同一个被更新了一次又一次
结果没时间做C了,光荣垫底orz orz orz
C题即使是在考试之后得知Tarjan也没做出来orz orz orz
果然还是我太弱了(反思),平时做的题也太少了,过于依赖讲义,评讲什么的,,,,,一到自己做题就完全懵逼了,,,orz orz orz
A
显然时间等于走的步数
观察一下样例2
发现只用用step2,x2,y2记录一个上一次的步数和坐标,再与这一次的step,x,y比较一下,如果(step - step2) < abs(x2 - x) + abs(y2 - y)
就标记一个flag = 1;
观察样例3
if((step2 - (x + y)) % 2)
也是不行的,也需要标记flag = 1(因为类似于“消磨时间”的走法一定是走偶数步,所以步数与位置的曼哈顿距离必须同奇偶)
边输入边扫,扫完之后看一下flag,如果等于0就输出Yes,等于1就输出No
B
一个最简单的NKOJ上的背包问题1加背包问题2的联合
A,B分别为无限物品,水为仅能用一次的有限物品
具体过程
v[0] = true;
for(int i = A; i <= T; i ++)if(v[i - A])v[i] = true;
for(int i = B; i <= T; i ++)if(v[i - B])v[i] = true;
for(int i = 0; i <= T; i ++)if(v[i])v[i / 2] = true;
//注意一下,由于这里i是除法,也就是越更新越小,所以for循环从小到大枚举i
//举个栗子:假设f[6] = true,我们将f[3]也标记为true
for(int i = A; i <= T; i ++)if(v[i - A])v[i] = true;
for(int i = B; i <= T; i ++)if(v[i - B])v[i] = true;
//再来一遍就可以了orz orz orz
C
每个人都有一个固定的信息传递对象 —> 每个人的出度都是1
找最少进行几轮可以转换成最少经过几个人—>找最小环
因为每个人的出度都是1,所以不会出现环套环的情况,即可以求最小环
解法:用Tarjan找强连通分量,每一个强连通分量都是一个环,在从栈里面弹出点的时候记录一下每一个强连通分量内点的个数,只要不是1就比较出最小的一个
D
一道没有人通过的水题orz(当然我是完全不会的)
如果只有一种球:逆序对(凡是交换相邻两个的都是求逆序对) —> 两种球:两个逆序对orz
设白球:1 ~ i个
黑球:1 ~ j个
无论怎么排,一定会占满2n个里面前i + j个位置
所以可以设一个状态f[i][j],表示前i加j个位置中放白球前i个,黑球前j个,所需最少交换次数
决策:i + j号位置放i号白球还是j号黑球
方程:f[i][j] = min{f[i - 1][j] + cntWhite[i][j],f[i][j -1] + cntBlack[i][j]}
cntWhite[i][j] = ① + ②:
①[1,i - 1]白球中,位于i右侧的,都要调整到i左侧(逆序对)
②[1, j]黑球中,位于i右侧的,都要调整到i左侧(逆序对)
cntBlack[i][j] = ③ + ④:
③[1, i]白球中,位于j右侧的,都要调整到j左侧
④[1,j - 1]黑球中,位于j右侧的,都要调整到j左侧
如何求cnt?——暴力枚举!
cntW的求法
for(int i = 0; i <= n; i ++){
for(int j = 1; j < i; j ++)
if(posw[i] < posw[j]) cntW[i][0] ++;
for(int j = 1; j <= n; j ++){
cntW[i][j] = cntW[i][j - 1];
if(posw[i] < posb[j]) cntW[i][j] ++;
}
}
cntB的求法
for(int i = 0; i <= n; i ++){
for(int j = 1; j < i; j ++)
if(posb[i] < posb[j]) cntB[0][i] ++;
for(int j = 1; j <= n; j ++){
cntB[j][i] = cntB[j - 1][i];
if(posb[i] < posw[j])cntB[j][i] ++;
}
}
一般来说数组是存对应位置是哪个球
但是这里我们存的是对应编号是哪个位置
感觉是这辈子自己都想不出来的orz orz orz
AC代码如下
A
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
int n, x, y, step1, step2;
int lastx, lasty;
bool flag = false;
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i ++){
scanf("%d%d%d", &step2, &x, &y);
if((step2 - step1) < abs(lastx - x) + abs(lasty - y))flag = true;
else if((step2 - x - y) % 2)flag = true;
step1 = step2;
lastx = x;
lasty = y;
}
if(flag) puts("No");
else puts("Yes");
return 0;
}
B
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
int T, A, B, Max = 0;
bool v[5000005];
int main(){
v[0]=true;
scanf("%d%d%d", &T, &A, &B);
if(!(T % A)|| !(T % B)){
printf("%d\n",T);
return 0;
}
for(int i = A; i <= T; i ++) if(v[i - A])v[i] = 1;
for(int i = B; i <= T; i ++) if(v[i - B])v[i] = 1;
for(int i = 1; i <= T; i ++) if(v[i]) v[i / 2] = true;
for(int i = A; i <= T; i ++) if(v[i - A])v[i] = 1;
for(int i = B; i <= T; i ++) if(v[i - B])v[i] = 1;
for(int i = T; i >= 1; i --){
if(v[i]){printf("%d",i);
return 0;
}
}
C
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
stack<int>S;
bool InStack[200005];
int n, a, scc, tot;
int VisitTime, MIN = 9999999;
int End[200005], Last[200005], Next[200005];
int low[200005], dfn[200005], Belong[200005];
int Min(int a, int b){
return a < b ? a : b;
}
void tajian(int u){
int nonstop = 0;
low[u] = dfn[u] = ++ VisitTime;
S.push(u);
InStack[u] = true;
for(int i = Last[u]; i; i = Next[i]){
int v = End[i];
if(!dfn[v]){
tajian(v);
low[u] = Min(low[u], low[v]);
}
else if(InStack[v]){
low[u] = Min(low[u], low[v]);
}
}
if(dfn[u] == low[u]){
scc ++;
int v;
do{
v = S.top();
S.pop();
InStack[v] = false;
Belong[v] = scc;
nonstop ++;
}while(u != v);
if(nonstop != 1)MIN = Min(nonstop, MIN);
}
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i ++){
scanf("%d", &a);
End[i] = a;
Next[i] = Last[i];
Last[i] = i;
}
for(int i = 1; i <= n; i ++)if(!dfn[i])tajian(i);
printf("%d", MIN);
}
D
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
char c;
int n, x;
int posw[2005], posb[2005];
int cntW[2005][2005],cntB[2005][2005],f[2005][2005];
int main()
{
memset(f,0x7f,sizeof(f));
cin>>n;
for(int i = 1; i <= 2 * n; i ++){
cin>>c>>x;
if(c == 'W') posw[x] = i;
else posb[x] = i;
}
for(int i = 0; i <= n; i ++){//i从0开始!
for(int j = 1; j < i; j ++) if(posw[i] < posw[j]) cntW[i][0] ++;
for(int j = 1; j <= n; j ++) cntW[i][j] = cntW[i][j - 1] + (posw[i] < posb[j]);
}
for(int i = 0; i <= n; i ++){
for(int j = 1; j < i; j ++) if(posb[i] < posb[j]) cntB[0][i] ++;
for(int j = 1; j <= n; j ++) cntB[j][i] = cntB[j - 1][i] + (posb[i] < posw[j]);
}
f[0][0] = 0;//~赋~初~值~
for(int i = 0; i <= n; i ++)
for (int j = 0; j <= n; j ++)
{
if(i)f[i][j] = min(f[i - 1][j] + cntW[i][j], f[i][j]);
if(j)f[i][j] = min(f[i][j - 1] + cntB[i][j], f[i][j]);
}
printf("%d\n",f[n][n]);
return 0;
}