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
", 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 imes 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
", Max);
		my_tong[a[i]] ++;   //读入每一个人的时候就丢进桶
		int num = my_max(1, i * m / 100);
//		printf("%d
", 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大巨佬里面第一列只能由上向下走还愣了一愣,画了一下图才理解
第一列.png
这样路径无论如何都会重复

#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。虽然考了一个不太理想的成绩,但给笔者带来的收获是分数无法估量的。

夜空中最亮的星,请照亮我前行
原文地址:https://www.cnblogs.com/Nefelibata/p/13971215.html