大一上第二周学习笔记

9.22 周二

学习一定要清楚地知道自己学了什么,用这个博客记录一下自己究竟学了什么,不要一塌糊涂

算是一个反思总结吧。做题不要盲目做,做完花个5分钟反思总结一下到底学到了什么,列个一二三,这样印象更深。以后也可以回来复习

一道题一道题扎扎实实地独立做出并总结,这样稳步提升。算是费曼学习法的实践,把题目讲出来。

这个博客算是写给自己看的。

两年没碰了,现在阶段算是恢复

慢慢建立起学习体系和方法

 

洛谷 P2181 对角线

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

typedef unsigned long long ull;

int main()
{
	ull a;
	scanf("%llu", &a);
	printf("%llu", a * (a-1) / 2 * (a-2) / 3 * (a-3) / 4);
	return 0;
}

一 没有独立做出。这次找了个借口说我现在在恢复而不是干数学。数学部分没想太多,就看了题解。这样非常不好,每一次写题都要独立写出,不要找借口。至少刚两天还做不出才看题解

二 超时的话算一下在10的九次方内就可以

三 这个公式计算会爆,这是这道题难点。

首先是不要一下全部乘完,而是一边乘一边除,而且判断可以除尽

然后是变量类型。

int   1e9

long long  1e18

unsigned long long 1e19

题目1e5,用公式算出大概是1e19

所以要用unsigned long long   scanf和printf用llu

 

int gcd(int a, int b) { return !b ? a: gcd(b, a % b); }

复习gcd

 

9.23 周三

 

洛谷 P5740 【深基7.例9】最厉害的学生

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)

const int MAXN = 1000 + 10;
struct node
{
	char name[10];
	int a, b, c;
}in[MAXN];

int main()
{
	int n, sum = 0, ans;
	scanf("%d", &n);
	REP(i, 0, n)
	{
		scanf("%s%d%d%d", in[i].name, &in[i].a, &in[i].b, &in[i].c);
		if(in[i].a + in[i].b + in[i].c > sum)
		{
			sum = in[i].a + in[i].b + in[i].c;
			ans = i;
		}
	}
	printf("%s %d %d %d\n", in[ans].name, in[ans].a, in[ans].b, in[ans].c);
	return 0;
}

复习结构体

把之前熟悉的知识一点点捡起来

 

洛谷 P5723 【深基4.例13】质数口袋

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 1e5 + 10;
bool prime[MAXN];

void init()
{
	memset(prime, true, sizeof(prime));
	prime[0] = prime[1] = false;
	REP(i, 2, MAXN)
		if(prime[i])
			for(int j = i * 2; j < MAXN; j += i)
				prime[j] = false;
}

int main()
{
	init();
	int L, ans = 0, sum = 0;
	scanf("%d", &L);
	REP(i, 2, MAXN)
		if(prime[i])
		{
			if(sum + i > L) break;
			sum += i;
			ans++;
			printf("%d\n", i);
		}
	printf("%d\n", ans);
	return 0;
}
 

复习素数筛

这种写法容易理解,也比较好记

先初始化,然后素数的倍数都不是素数就好了

复杂度nloglogn,非常接近n了

 

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 100 + 10;
struct node
{
	int a, b;
}k[MAXN];

bool cmp(node x, node y)
{
	return x.a < y.a || (x.a == y.a && x.b < y.b);
}

int main()
{
	int n;
	scanf("%d", &n);
	REP(i, 0, n) scanf("%d%d", &k[i].a, &k[i].b);
	sort(k, k + n, cmp);
	REP(i, 0, n) printf("%d %d\n", k[i].a, k[i].b);
	return 0;
}

复习sort,结构体排序

 

洛谷 P1601 A+B Problem(高精)

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 1000 + 10;
int a[MAXN], b[MAXN];
char t1[MAXN], t2[MAXN];
int len1, len2, len;

int main()
{
	scanf("%s", t1); len1 = strlen(t1);
	for(int i = len1 - 1; i >= 0; i--) a[i] = t1[len1-i-1] - '0';
	scanf("%s", t2); len2 = strlen(t2);
	for(int i = len2 - 1; i >= 0; i--) b[i] = t2[len2-i-1] - '0';
	
	len = max(len1, len2);
	REP(i, 0, len)
	{
		a[i] += b[i];
		a[i+1] += a[i] / 10;
		a[i] %= 10;
	}
	if(a[len]) len++;
	
	for(int i = len - 1; i >= 0; i--) printf("%d", a[i]);
	
	return 0;
}

复习高精度加法

一 注意要反过来存入,反过来输出

二 字符串一般输入用scanf

一行带空格的字符要输入才用gets

scanf会在空格终止,而gets会读入空格

 

洛谷 P1303 A*B Problem 题解

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 4000 + 10;
int a[MAXN], b[MAXN], c[MAXN];
char t1[MAXN], t2[MAXN];
int len1, len2, len;

int main()
{
	scanf("%s", t1); len1 = strlen(t1);
	for(int i = len1 - 1; i >= 0; i--) a[i] = t1[len1-i-1] - '0';
	scanf("%s", t2); len2 = strlen(t2);
	for(int i = len2 - 1; i >= 0; i--) b[i] = t2[len2-i-1] - '0';
	
	REP(i, 0, len1)
		REP(j, 0, len2)
			c[i+j] += a[i] * b[j];
			
	len = 1;
	while(1)
	{
		c[len] += c[len-1] / 10;
		c[len-1] %= 10;
		if(c[len]) len++;
		else break; 
	}
	for(int i = len - 1; i >= 0; i--) printf("%d", c[i]);
	
	return 0;
}

一 自己测试的时候不但样例要过,还要考虑特殊情况。比如这道题的乘上0,那么答案的位数的情况就不一样了

二 进位问题。边算边进位和最后一起进位结果是一样的。最后答案的位数处理比较细节。最后一次性处理笔记方便。如果边算边进位的话,还要考虑最高位是否为0以及最高位要不要进位

三 模板题可以看例题,别人的代码学习,其他情况下绝对要独立自己写出来

 

今日的学习部分结束了

一 时间安排上。大一上课不多。周一到周五就是自己把课表填满。晚上做其他事情

二 毕竟两年没碰了,现在慢慢恢复实力。明天在刚一道阶乘的题目,高精度就告一段落了。

还行,就这样一步一步扎扎实实地提升。我不会再像高中那样抄题解麻痹自己,我会认认真真独立完成题目。加油。

 

9.24 周四

 

洛谷 P1009 阶乘之和

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 10000 + 10;
int sum[MAXN], t[MAXN], lens, lent;

void add()
{
	lens = max(lens, lent);
	REP(i, 0, lens)
	{
		sum[i] += t[i];
		sum[i+1] += sum[i] / 10;
		sum[i] %= 10;
	}
	if(sum[lens]) lens++;
}

void mul(int k)
{
	REP(i, 0, lent) t[i] *= k;
	REP(i, 0, lent) 
	{
		t[i+1] += t[i] / 10;
		t[i] %= 10;
	}
	
	while(t[lent]) 
	{
		t[lent+1] += t[lent] / 10;
		t[lent] %= 10;
		lent++;
	}
}

int main()
{
	int n;
	scanf("%d", &n);
	
	lens = lent = 1;
	t[0] = 1;
	
	_for(i, 1, n)
	{
		mul(i);
		//printf("i: %d lent: %d\n", i, lent);
		//for(int i = lent - 1; i >= 0; i--) printf("%d", t[i]); 
		//puts("");
		add();
	}
	for(int i = lens - 1; i >= 0; i--) printf("%d", sum[i]);
	
	return 0;
}

一 高精乘低精时,要不用两个数组,要不乘完再一起进位
二 进位时用while,和加法不一样,乘法可能进多位。同时进位时注意+1-1的问题,想清楚 

 

9.25 周五

洛谷 P1255 数楼梯

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 2000;
int f[5][MAXN], len[5];

void add(int k)
{
	int p1 = (k-2) % 3, p2 = (k-1) % 3, p3 = k % 3;
	memset(f[p3], 0, sizeof(f[p3]));
	len[p3] = max(len[p2], len[p1]);
	REP(i, 0, len[p3])
	{
		f[p3][i] += f[p1][i] + f[p2][i];
		f[p3][i+1] += f[p3][i] / 10;
		f[p3][i] %= 10;
	}
	if(f[p3][len[p3]]) len[p3]++;
}

int main()
{
	int n;
	scanf("%d", &n);
	if(n <= 2)
	{
		printf("%d", n);
		return 0;
	}
	f[1][0] = 1; f[2][0] = 2;
	len[1] = len[2] = 1;
	
	_for(i, 3, n) add(i);
	for(int i = len[n%3] - 1; i >= 0; i--) printf("%d", f[n%3][i]);
	puts("");
	
	return 0;
}

这道题独立做出,还是学到很多的

题目要做精,一要独立做出,二做完后学习别人的做法,吸收优点

虽然时间会长一点,但这样其实效率更高,一道题一道题百分百掌握,踏踏实实积累,实力稳步提升

 

总结

一 自己尝试极限数据 这里最大推出高精度,最小推出特判。

除了过样例,还要自己尝试极限数据。

数据一大就发现爆了,要高精度。数据小了发现0要特判,不然90

 

二 二维数组范围

我开始觉得5000 * 5000 空间会超吧。其实我对这个空间认识不清,一般来说3千万,所以5000 * 5000是极限的,空间不会超。

 

三  高精度的具体实现方法

有些题答案巨大要用到高精度。如果要写重载运算符的话好麻烦,那部分的语法我不懂。

所以要写高精度的时候还是上数组吧。

 

四 递归复杂度

一开始写的递归,然后超时。其实这个时间复杂度很高的,2的n次方

一般要控制在10的八次方,也就是一个亿左右才不会超时

这个5000肯定超


五 三个数组的mod实现方法 循环数组 

这里我为了省空间写了循环数组,核心是用mod 3 处理下标

 

洛谷 P1002 过河卒

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 30;
typedef long long ll;
ll f[MAXN][MAXN];
int dir[8][2] = {{1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {-2, 1}, {2, -1}, {-2, -1}};

int main()
{
	int x, y, n, m;
	scanf("%d%d%d%d", &n, &m, &x, &y);
	
	f[x][y] = -1;
	REP(i, 0, 8)
	{
		int tx = x + dir[i][0], ty = y + dir[i][1];
		if(tx < 0 || tx > n || ty < 0 || ty > m) continue;
		f[tx][ty] = -1;
	}
	
	if(f[0][0] == -1 || f[n][m] == -1) 
	{
		puts("0");
		return 0;
	}
	
	f[0][0] = 1;
	_for(i, 0, n)
		_for(j, 0, m)
			if(f[i][j] != -1)
			{
				if(i == 0 && j == 0) continue;
				ll a = i == 0 ? 0 : max(f[i-1][j], ll(0));
				ll b = j == 0 ? 0 : max(f[i][j-1], ll(0));
				f[i][j] = a + b;
			}
	printf("%lld\n", f[n][m]);
	
	return 0;
}


一 尽量一次AC
提交前就考虑周全
因为比赛时是不能给你这么清楚的反馈的。
除了过样例,还要自己设一些难一些的数据测试,才能发现问题 

比如这里用极限数据发现要用long long

 

二 答案出现负数大概率是爆int了 

这种递推有爆int的可能,自己设数据尝试一下

 

三 马方向数组 

 

9.26 周六

 

洛谷 P1028 数的计算

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 1000 + 10;
int a[MAXN];

int f(int x)
{
	if(a[x]) return a[x];
	int t = 1;
	for(int i = x / 2; i >= 1; i--) t += f(i);
	return a[x] = t;
}

int main()
{
	int n;
	scanf("%d", &n);
	a[1] = 1;
	_for(i, 2, n) f(i);
	printf("%d\n", a[n]);
	return 0;
}

一开始写递归,输入1000,发现会超时

这时发现可以记忆化,就记忆化了一下

然后就ac了

这时候我又去看别人怎么做的,发现别人是递推

其实很多时候记忆化搜索就可以写成递推

我写复杂了,写递推更简洁一些。

不过也算复习了一下记忆化搜索

发现前面的状态可以用前面求出的结果表示时,就用递推

 

洛谷 P1164 小A点菜

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 1000 + 10;
int a[MAXN], ans, n, m;

void f(int sum, int k)
{
//	printf("sum: %d k: %d ans: %d\n", sum, k, ans);
	REP(i, k, n)
	{
		if(a[i] > sum) return;
		if(a[i] == sum) ans++;
		else f(sum - a[i], i + 1);
	}
}

int main()
{
	scanf("%d%d", &n, &m);
	REP(i, 0, n) scanf("%d", &a[i]);
	
	sort(a, a + n);
	f(m, 0);
	printf("%d\n", ans);
	
	return 0;
}

dp以后再复习,太久了现在不熟悉,写了搜索

最近题目新加了一组数据把搜索卡了一个点

 

洛谷 P1464 Function(记忆化搜索)

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 25;
typedef long long ll;
ll f[MAXN][MAXN][MAXN];

ll w(ll a, ll b, ll c)
{
	if(a <= 0 || b <= 0 || c <= 0) return 1;
	if(a > 20 || b > 20 || c > 20) return w(20, 20, 20);
	if(f[a][b][c]) return f[a][b][c];
	if(a < b && b < c) return f[a][b][c] = w(a, b, c-1) + w(a, b-1, c-1) - w(a, b-1, c);
	return f[a][b][c] = w(a-1, b, c) + w(a-1, b-1, c) + w(a-1, b, c-1) - w(a-1, b-1, c-1);
}

int main()
{
	ll a, b, c;
	while(scanf("%lld%lld%lld", &a, &b, &c))
	{
		if(a == -1 && b == -1 && c == -1) break;
		printf("w(%lld, %lld, %lld) = %lld\n", a, b, c, w(a, b, c));
	}
	return 0;
}

一 不要只满足过样例,过完样例要停一停自己出一些数据,尤其是数据好出的时候。这里一出极限数据发现要用longlong

二 记忆化搜索和动态规划很像,但可以避免一些无用状态,可以剪枝,而动态规划要遍历所有状态

 

9.27 周日

 

洛谷 P1044 栈(卡特兰数复习)

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 20;
int h[MAXN];

int main()
{
	h[0] = h[1] = 1;
	_for(i, 2, 18)
		_for(j, 0, i - 1)
			h[i] += h[j] * h[i-j-1];
	
	int n;
	scanf("%d", &n);
	printf("%d\n", h[n]);
	
	return 0;
}

 

想了好久,没有突破,不过在这过程中锻炼了思维能力

想到了分成两段,但是我分段的标准是最后一个数

实际上应该是最后一个出栈的数,这样非常巧妙地可以转化为两个已经求过的状态

太巧了

实际上就是卡特兰数,之前我还专门学过,现在两年多过去已经忘记了

实力下降太厉害了

复习卡特兰数,这是一类问题

卡特兰数总结

这个写的非常好

 

一 数列是 1 1 2 5 14 42…… 如果看到前几项是这个,就可以猜是卡特兰数了

二 出栈问题是经典,写出递推式h(0) = h(1) = 1    h(n) = h(0) * h(n-1) + h(1) * (n-2)……h(n-1) * h(0) 

也可以写成 C(2n, n)/ (n + 1)

以后自己推出这个式子就是卡特兰数

三  出栈问题可以有很多变形,本质都是01序列问题

给出一个n,要求一个长度为2n的01序列,使得序列的任意前缀中1的个数不少于0的个数, 有多少个不同的01序列?

 

洛谷 P2249 【深基13.例1】查找

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 1e6 + 10;
int a[MAXN];

int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	_for(i, 1, n) scanf("%d", &a[i]);
	
	while(m--)
	{
		int q, mid, l = 0, r = n + 1;
		scanf("%d", &q);
		
		while(l + 1 < r)
		{
			mid = (l + r) >> 1;
			if(a[mid] >= q) r = mid;
			else l = mid;
		} 
		
		if(a[r] != q) printf("-1 ");
		else  printf("%d ", r);
	}
	
	return 0;
}

开始复习二分

自己以前写的东西对现在的我有非常大的帮助

原来这是写博客的好处

二分总结

 

这道题是裸题

把我之前总结的二分稍微改一下就好

脑子里清楚l, r, mid

 

洛谷 P1102 A-B 数对

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 2e5 + 10;
int a[MAXN], n, c; 
long long ans;

int f(int p)
{
	int key, l, r;
	key = a[p] + c;
	l = lower_bound(a + p + 1, a + n + 1, key) - a;
	r = upper_bound(a + p + 1, a + n + 1, key) - a;
	return r - l;
}

int main()
{
	scanf("%d%d", &n, &c);
	_for(i, 1, n) scanf("%d", &a[i]);
	sort(a + 1, a + n + 1);
	_for(i, 1, n) ans += f(i);
	printf("%lld\n", ans);
	return 0;
}

一  二分可以用lower_bound和upper_bound,很方便,不用手写

二 我一开始一直有一个点过不了。后来发现是ans要开long long

我为数据范围还是不太敏感

这里其实可以有很多很多种数对。因此ans要开long long

 

洛谷 P1873 砍树

#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;

typedef long long ll;
const int MAXN = 1e6 + 10;
int h[MAXN], n, m; 

bool check(int k)
{
	ll sum = 0;
	_for(i, 1, n) sum += max(h[i] - k, 0);
	return sum >= m;
}

int main()
{
	int l = 0,  r = 0, mid;
	scanf("%d%d", &n, &m);
	_for(i, 1, n) 
	{
		scanf("%d", &h[i]);
		r = max(r, h[i]);
	}
	
	while(l + 1 < r)
	{
		mid = (l + r) >> 1;
		if(!check(mid)) r = mid;
		else l = mid;
	}
	printf("%d\n", l);
	
	return 0;
}

二分答案模板题

 

今天量挺大的

最后一个小时左右效率变低了,脑子有点不想动了

那今天就这样吧

明天弄弄 解方程,两道二分答案题,二分就过了

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值