csp2020_j(心路历程 + 题解(T3未A) + 总结)
比赛心路历程
其实这次比赛笔者心态还是蛮好的。(或许是平时太弱爆0出锅太多次了,以至于对于这种事看得很开???)
刚开始先(听gm的话)把所有的题目都过了一遍,然后就开始按照T1 \(\Rightarrow\) T2 \(\Rightarrow\) T4\(\Rightarrow\) T3\(\Rightarrow\)的顺序开始了尝试。
T1把所有大样例都过了一遍之后,就去打T2,T2很明显就是个排序嘛,但n的数据范围.....于是考场上就开始想优化,突然想起了每位选手的分都\(0 \geqslant\)且\(\geqslant 600\)于是就非常自然的想到了桶排序。
在打了T2后,又改了改,过了所有的大样例后就松了一口气(现在想起我不是松了一口气,而是差点在这儿断气,除了前面的200pts,后面都出锅了)。然后就去肝T4,看到这个题面,我很自然的就想到了一道经典例题:数字三角形。但是还可以从下往上走的方式蒙蔽住了我的双眼,尽管第六感告诉我应该是道DP,(至少不是我最长路的做法),但笔者还是毅然决然的选择用最长路去骗分(大致采用的堆优化的dijkstra)。虽然被第3组大样例卡住了。但笔者还是做T3去了。看到T3的时候,完全没有头绪,而且时间也不多了。就瞎打了一个暴力打算骗取30pts。本来过掉了样例1,但因为后来的魔改,加时间到。连样例1都过不了了。
其实这次比赛还闹了一个乌龙的:比赛前笔者的桌子上出现了一只体型微小的蟑螂就暂且不论,更主要的是笔者脑残用记事本打开样例,结果看不到换行,在询问监考老师无果后,考场上临时总结出了双击看不到换行,单击后再点查看才有换行的经验。
笔者好啰嗦呀
题解
T1
传送门
其实就是一道纯暴力,也是本次csp的打卡题。就不多赘述了
//考场上的代码加注释(已去freopen)
#include <cstdio>
using namespace std;
long long n;
long long m_2(int x){ //计算2^x
long long sum = 1;
for(int i = 1; i <= x; i ++){
sum *= 2;
}
return sum;
}
void my_read_int(int &x){ //未压行的读优
int f = 1;
char flag;
flag = getchar();
while(flag > '9' || flag < '0'){
if(flag == '-'){
f = -1;
}
flag = getchar();
}
while(flag >= '0' && flag <= '9'){
x = x * 10;
x += flag - '0';
flag = getchar();
}
x *= f;
}
void my_read_longlong(long long &x){ //同上
int f = 1;
char flag;
flag = getchar();
while(flag > '9' || flag < '0'){
if(flag == '-'){
f = -1;
}
flag = getchar();
}
while(flag >= '0' && flag <= '9'){
x = x * 10;
x += flag - '0';
flag = getchar();
}
x *= f;
}
int main() {
my_read_longlong(n);
long long flag;
flag = n;
int cnt = 0;
if(n & 1){ //判断是否为奇数
printf("-1");
return 0;
}
else{
while(flag){ //计算在二进制下最多有几位
cnt ++;
flag >>= 1;
}
}
flag = n;
// int b;
// scanf("%d", &b);
// printf("%d\n", m_2(b));
for(int i = cnt; i >= 1; i --){ //判断在二进制下某位是否为1
long long m_i = m_2(i);
if(flag >= m_i){
printf("%d ", m_i);
flag -= m_i;
}
}
return 0;
}
T2
传送门
其实在刚刚心路历程也写了,n的数据范围为 100000,如果打暴力用sort的话也在\(O_{n^2 \times log^n_2}\)左右,于是考场上就开始想优化,突然想起了每位选手的分都\(0 \geqslant\)且\(\geqslant 600\)于是就非常自然的想到了桶排序。。而且时间复杂的大概在\(O_{600n}\)。完全不会TLE(除非打锅)
//考场上的代码加注释(已去freopen)
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e5 + 5;
int my_tong[1005];
int a[maxn];
int n, m;
int my_max(int x, int y){
return x > y ? x : y; //自己写的max函数时间复杂度要小一点
}
int Max = 0;
void my_read_int(int &x){ //日常读优
int f = 1;
char flag;
flag = getchar();
while(flag > '9' || flag < '0'){
if(flag == '-'){
f = -1;
}
flag = getchar();
}
while(flag >= '0' && flag <= '9'){
x = x * 10;
x += flag - '0';
flag = getchar();
}
x *= f;
}
int main(){
memset(my_tong, 0, sizeof(my_tong)); //其实比赛用memset还不如暴力清空快,只是因为我这的数组比较小才用的(最好还是不用)
my_read_int(n);
my_read_int(m);
for(int i = 1; i <= n; i ++){
my_read_int(a[i]);
Max = my_max(a[i], Max); //为了优化桶,循环中计算当前最大值
// printf("%d\n", Max);
my_tong[a[i]] ++; //读入每一个人的时候就丢进桶
int num = my_max(1, i * m / 100);
// printf("%d\n", num);
int flag = 0, flag_num;
for(int i = Max; i >= 0; i --){ //从最大值开始倒着累加知道人数够了
if(flag >= num){
break;
}
flag += my_tong[i];
// printf("%d ", my_tong[i]);
flag_num = i;
}
printf("%d ", flag_num);
}
return 0;
}
T3
因为笔者太弱了,肝了一个多小时的表达式建树,加后面的判断还没有肝完(确实对于这一块是空白的),所以先咕咕咕着吧,为给您带来不太愉快的观感而感到抱歉QAQ。
T4
其实比赛的时候想到了DP的(联想到了数字三角形),但还是被可以向上走这一条件蒙蔽住了双眼,本来抱着骗骗至少普一就没有问题了的(不纯粹的)心态,瞎打了一个最长路(堆优化的dijkstra)。
结果还开大了MLE掉了(你谷和牛客都是)。
考完了参考了lym大巨佬的题解,才发现好妙啊~ %%%lym
用两个dp数组来存储,一个最后一步向上,一个最后一步向下,就避免了重复的情况了。
ps:其实刚开始笔者看见lym大巨佬里面第一列只能由上向下走还愣了一愣,画了一下图才理解
这样路径无论如何都会重复
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e3 + 5;
long long dp1[maxn][maxn];
long long dp2[maxn][maxn];
int a[maxn][maxn];
int n, m;
int main() {
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= m; j ++){
scanf("%d", &a[i][j]);
}
}
memset(dp1, -0x3f3f3f3f, sizeof(dp1));
memset(dp2, -0x3f3f3f3f, sizeof(dp2));
dp1[1][1] = a[1][1]; //对于dp1[1][1]赋初值
for(int i = 2; i <= n; i ++){
dp1[i][1] = dp2[i][1] = dp1[i - 1][1] + a[i][1]; //对第一列赋初值,原理如上图
}
for(int j = 2; j <= m; j ++){ //因为不同的状态是由最后一步向上或向下决定的,所以第一重循环枚举j
for(int i = 1; i <= n; i ++){ //处理从上往下和从左的情况
dp1[i][j] = dp2[i][j] = max(dp1[i][j - 1], dp2[i][j - 1]) + a[i][j];
dp1[i][j] = max(dp1[i - 1][j] + a[i][j], dp1[i][j]);
}
for(int i = n; i >= 1; i --){
dp2[i][j] = max(dp2[i + 1][j] + a[i][j], dp2[i][j]);反向处理从下往上的情况
}
}
printf("%lld", max(dp1[n][m], dp2[n][m]));
return 0;
}
feedback
其实这场比赛给笔者的收获还蛮大的,增长见识是次要的(虽然这是我参加的第一场正式的oi比赛),更重要的是让我深刻的意识到了自己的不足以及和大巨佬们的差距,还让笔者发现了自己在某些学习品质上的欠缺另一篇blog。虽然考了一个不太理想的成绩,但给笔者带来的收获是分数无法估量的。