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

    夜空中最亮的星,请照亮我前行
  • 相关阅读:
    HDU 5023 A Corrupt Mayor's Performance Art(线段树区间更新)
    CSU 1120 病毒(DP)
    CSU 1116 Kingdoms(枚举最小生成树)
    一种map容器遍历的方法
    CSU 1113 Updating a Dictionary(map容器应用)
    Python模块-virtualenv-虚拟环境
    计算学习原理
    sklearn-特征工程之特征选择
    链家网深圳租房信息分析报告
    test
  • 原文地址:https://www.cnblogs.com/Nefelibata/p/13971215.html
Copyright © 2011-2022 走看看