总结
- 七月份信息课总结
- 算法记录
- 线性代数:
- 数论(这是信竞生和数竞生都最难跨出的一步):
- 动态规划(~~DP万岁!!!~~)
- 组合数学(恶心,但我很喜欢)
- 贪心
- 好题记录
- [P1962 斐波那契数列](https://www.luogu.com.cn/problem/P1962)
- [P6064 [USACO05JAN] Naptime G](https://www.luogu.com.cn/problem/P6064)
- [P1223 排队接水](https://www.luogu.com.cn/problem/P1223)
- 总结
七月份信息课总结
算法记录
线性代数:
线性代数是一种用线性思维解决代数问题的方法,常见的有向量、矩阵等。学习过程中,老师讲解了点乘、叉乘以及向量用矩阵的表示方法,还有矩阵快速幂在加速斐波那契的实现。这个知识点与数学的关系很强,需要良好的数学基础和活跃的数学思维。现在的题目可能局限于二维向量,将来可能会用到三位向量,所以还需熟练掌握三维向量的计算与运用。
数论(这是信竞生和数竞生都最难跨出的一步):
其实,我对数论的理解也只是云里雾里。目前只是背下几个板子,然后对一些简单的少有一点点理解。我的数学老师说他数论没学好,所以不能跟我讲(dog) 所以说,目前对数论的学习还是停留在背诵板子
动态规划(DP万岁!!!)
以前认为DP很难,但是后面随着数学基础的加深,发现其实DP并不难。我甚至发现了一条万能的DP转移方程(当然是在不考虑优化的情况下)即:有多少个未知量就定义多少维度(数组开不下的话可以考虑vector,因为vector可以根据每次输入的值进行开数组的大小,可以多骗一点分),其值就是要求的值。这样一来,至少DP题目的50%分数是可以得到的。并且,对后期的优化也很有帮助。实际上,还可以用记搜,MLE别找我(dog)
组合数学(恶心,但我很喜欢)
记得OI上有一句经典的名言“组合数学瞎暴力”,但其实组合数学并不恐惧。组合数学无非4种:加法原理,乘法原理,组合,排列。当有多个物体进行排列(组合)时,每个物品的排列(组合)数相乘,是乘法原理;当求不同阶段的排列(组合)数之和时,排列(组合)数相加,是加法原理。在乎顺序是排列,否则是组合。我们只需根据题目进行分析,问题就可以迎刃而解了
贪心
记得刚刚学OI时,第一次接触贪心,就将其与DP混淆,其实贪心的决策是一个阶段性的决策,而DP是一个全局性的决策。DP强调的是两个字“全局”,所以说DP的题目也满足一个前置要求:无后效性(以后的决策不能影响当前的状态);而贪心强调当前,所以他不在乎以后如何。(例如:左边地上有100元,但是100元后只有1分钱,右边地上有1块钱,但是之后有1000元;贪心会选择左边,而DP会选择右边)所以说,只有那种全局要最优值一定要当前最优值的才能用贪心算法
好题记录
P1962 斐波那契数列
这一题是运用了矩阵快速幂优化斐波那契。我们知道:
F
i
−
1
+
F
i
−
2
=
F
i
F_{i-1}+F_{i-2}=F_i
Fi−1+Fi−2=Fi
然后可以推出
[
F
n
−
1
F
n
−
2
]
∗
[
1
1
1
0
]
=
[
F
n
F
n
−
1
]
\begin{equation*}\begin{bmatrix}F_{n-1}&F_{n-2}\end{bmatrix}\end{equation*}*\begin{equation*}\begin{bmatrix} 1& 1\\1 & 0\end{bmatrix}\end{equation*}= \left[\begin{array}{c}F_n & F_{n-1}\end{array}\right]
[Fn−1Fn−2]∗[1110]=[FnFn−1]
所以:
标算:
#include<iostream>
#include<string.h>
using namespace std;
#define ll long long
const ll MOD=1e9+7;
ll n;
struct mat{
ll a[3][3];
mat(){memset(a,0,sizeof(a));}
mat operator*(const mat &b){
mat res;
for(ll i=1;i<=2;i++){
for(ll j=1;j<=2;j++){
for(ll k=1;k<=2;k++){
res.a[i][j] = (res.a[i][j] + a[i][k] * b.a[k][j]) % MOD;
}
}
}
return res;
}
}ans,base;
void init(){
base.a[1][1]=base.a[1][2]=base.a[2][1]=1;
ans.a[1][1]=ans.a[1][2]=1;
}
void q(ll b){
while(b){
if(b&1)ans=ans*base;
base=base*base;
b>>=1;
}
}
int main(){
cin>>n;
if(n<=2){
cout<<1<<"\n";
return 0;
}
init();
q(n-2);
cout<<ans.a[1][1]%MOD<<"\n";
return 0;
}
P6064 [USACO05JAN] Naptime G
这是一道DP的题目,看到题目首先发现有三个未知量:当前时间、已睡觉时长、当前是否睡,要求的是效用值之和,所以就可以推出状态转移方程:
d
p
[
i
]
[
j
]
[
0
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
]
[
0
]
,
d
p
[
i
−
1
]
[
j
]
[
1
]
)
d
p
[
i
]
[
j
]
[
1
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
−
1
]
[
0
]
,
d
p
[
i
−
1
]
[
j
−
1
]
[
1
]
+
u
i
)
dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1])\\dp[i][j][1]=max(dp[i-1][j-1][0],dp[i-1][j-1][1]+u_i)
dp[i][j][0]=max(dp[i−1][j][0],dp[i−1][j][1])dp[i][j][1]=max(dp[i−1][j−1][0],dp[i−1][j−1][1]+ui)
i
i
i表示当前是第
i
i
i个小时,
j
j
j表示已睡
j
j
j小时,
0
0
0表示当前没睡,
1
1
1表示当前这个小时在睡,所以这个问题就解决啦
【注意】:由于不知道最后一小时是不是在睡,所以第一个小时睡觉的情况需要分类讨论
标算:
#include<iostream>
#include<string.h>
#include<cstdio>
using namespace std;
const int MAXN=3835;
int n,b;
int u[MAXN];
int f[MAXN][MAXN][2];
int ans;
int main(){
cin>>n>>b;
for(int i=1;i<=n;i++)cin>>u[i];
// for(int i=1;i<=n;i++)cout<<u[i]<<" ";
memset(f,-0x3f,sizeof(f));
f[1][1][1]=f[1][0][0]=0;
for(int i=2;i<=n;i++){
f[i][0][0]=f[i-1][0][0];
for(int j=1;j<=b;j++){
f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
f[i][j][1]=max(f[i-1][j-1][1]+u[i],f[i-1][j-1][0]);
}
}
ans=max(f[n][b][0],f[n][b][1]);
memset(f,-0x3f,sizeof(f));
f[1][1][1]=u[1];
f[1][0][0]=0;
for(int i=2;i<=n;i++){
f[i][0][0]=f[i-1][0][0];
for(int j=1;j<=b;j++){
f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
f[i][j][1]=max(f[i-1][j-1][1]+u[i],f[i-1][j-1][0]);
}
}
cout<<max(ans,f[n][b][1])<<"\n";//即判断最后一小时睡和不睡的最优值
return 0;
// cout<<ans<<"\n";
return 0;
}
P1223 排队接水
这是一道经典的贪心题,因为每个人的排队时间之和想要最少,就得让他们从小到大排序
直接上代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 1005;
struct node{
int date,num;
};
int n;
node ti[MAXN];
bool cmp(node x,node y){
return x.date<y.date;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> ti[i].date;
ti[i].num=i;
}
sort(ti + 1, ti + 1 + n,cmp);
long long ans = 0;
for (int i = 1; i <= n; i++)
cout << ti[i].num << " ";
cout << "\n";
for (int j = n ; j >= 1; j--)
{
int i = n - j;
ans += ti[i].date * j;
}
printf("%.2lf", ans*1.0 / n);
return 0;
}
这就是近几周的好题记录
总结
近两周刷体面较广,但存在不够精细化的问题。平时老师布置的问题要落到实处,才能刚进一步。