文章目录
The Fourth Week
集腋成裘,聚沙成塔。几秒钟虽然不长,却构成永恒长河中的伟大时代。——弗莱彻
一、前言
周一又是牛客,我们三个人各ac一道题,不过B题其实我不太会,所以ac的是c题,大家又一块讨论了很久F,最后发现没有特判,wa12次。特判这个事真的很难搞。
打了俩场CF,我就一个问题,这网站怎么这么不稳定,打到一半卡住之后死活登不上去,时间到了连网站都加载不出来,目前才八百分,打算剩下时间多打几场,冲个分。
周三排位,身体不太舒服打的我晕头转向的。希望下次状态好一点吧。
周五牛客,ac了俩道题,就没有然后了。
二、算法
1.dfs
<1>(牛客训练营6 C)
题解:
给定一个整数n,n次询问,输入一个数字k后,输出三个相加为k的各异的fbnq数,如若不存在输出-1。
先用一个数组存下fbnq数列,找出不大于k的最大数,存入数组b1,再重复此操作,能找到三个数即输出。b为记录
代码:
#include<bits/stdc++.h>
using namespace std;
int b;
long long int b1[10];
long long int fib[90]={1,2};
void fi()
{
for (int i = 2; i < 90; i++)
fib[i] = fib[i - 2] + fib[i - 1];
}
int big(long long int n)
{
int k;
for(k=0;k<90;k++)
if (fib[k + 1]>n)return k;
return 0;
}
void solu(long long int n, bool first)
{
int k = big(n);
long long int left = n - fib[k];
if (first)
{
if (!left)
{
b1[b++]=n;
return;
}
if (left>0)
solu(left, false);
b1[b++]=fib[k];
}
else
{
if (left>0)
solu(left, false);
b1[b++]=fib[k];
}
}
int main()
{
fi();
int t;
cin >> t;
while (t--)
{
long long int n;
cin >> n;
b=0;
solu(n, true);
if(b==3)
{
for(int i=0;i<3;i++)cout<<b1[i]<<" ";
}
else if(b<3)
{
for(int i=0;i<b;i++)cout<<b1[i]<<" ";
for(int i=0;i<3-b;i++)cout<<0<<" ";
}
else cout<<-1<<endl;
cout << endl;
}
return 0;
}
2.线性DP
<1>(洛谷 B3637)
一个线性DP的板子,直接贴代码了。
代码:
#include <bits/stdc++.h>
using namespace std;
int main () {
int n;
cin >> n;
long long int a[n];
int fi[5005] = {1}; //感觉明明跟这行代码用处是一样的
for (int i = 0 ; i < n ; i++) {
cin >> a[i];
}
for (int i = 0 ; i < n ; i++) {
fi[i] = 1; //不太理解,不写这一行代码才四十分
for (int j = 0; j < i ; j++) {
if ( a[i] > a[j] ) fi[i] = max ( fi[j]+1 , fi[i] );
}
}
int ans=0;
for (int i = 0; i < n; i++) {
ans = max (fi[i] , ans);
}
cout << ans;
}
<2>(洛谷 P1091)
其实跟上面一题差不多,但我愣是没做出来,别人的代码倒是一看就懂,自己又编写了一遍
题解:
题意要求在n个小朋友中抽取k个,使得剩余的小朋友形成一个先增后降的最大序列,输出k。用数组fi,ki分别储存最大上升子序列和最大下降子序列,最后在每个人的位置上比较一下,即可知道最大的情况k是多少。
代码:
#include <bits/stdc++.h>
using namespace std;
int main () {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n;
cin >> n;
int a[n];
int fi[n];
int ki[n];
for (int i = 0;i < n;i++) {
cin >> a[i];
}
for (int i = 0;i < n; i++) {
fi[i]=1;
for (int j =0 ;j < i;j++){
if (a[i] > a[j])fi[i] = max (fi[i],fi[j]+1);
}
}
for (int i = n-1;i >= 0; i--) {
ki[i]=1;
for (int j = n-1 ;j > i;j--){
if (a[i] > a[j])ki[i] = max (ki[i],ki[j]+1);
}
}
int ans=0;
for ( int i = 0; i < n; i++){
ans=max (ans,fi[i]+ki[i]-1);
}
cout << n-ans;
return 0;
}
3.Dijkstra算法
捋了好一会儿这个算法,好像是个贪心的演化,适用于多坐标不同路长求最短路径。求最短路径常见的三种方法是BFS,Dijkstra(迪杰斯特啦),Floyd(弗洛伊德)。
DIJKSTRA(G, s) {
UnReachSet = G.V
对于每一个点v设置 v.key = ∞
对于每一个点v设置 v.previous = ∅
对于源点s设置 s.key = 0
while UnReachSet ≠ ∅
v = EXTRACT-MIN(UnReachSet)
对于从v出发的每条边(v, u)
// 松弛边(v, u)
if u.key > v.key + weight(u, v)
u.key = v.key + weight(u, v)
u.previous = v
}
找了很久,但是吧但是,这个算法真的好难,以后再看看
4.其它
这里面的题共同点就是,明明代码都看得懂,没有什么技术难度,但是做的时候又确实没有想到会是这个地方有问题,根本不明白自己为什么会错。可能还是题做太少了?这类型题都不太遇到?
<1>(CF round927 C)
题解:
这题有一个思想,顺向思维longlong不够大,得逆向取余,见注释。
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
long long int t;
cin>>t;
while(t--){
long long int n,m;
cin>>n>>m;
long long int a[n];
long long int sum=1;
long long int b[n];
for(int i=0;i<n;i++) {
cin>>a[i]; //3%4 * 5%3 * 6*4=3*5*6 %4
} //直接全部乘法会爆long long
string s;
cin>>s; //倒着求余数
long long int l,r;
long long int bs=0;
for(int i=0;i<n-1;i++){
if(s[i]=='L')bs++;
}l=r=bs;
int wz=n-1;
b[wz]=a[bs]%m;
for(int i=n-2;i>=0;i--){
wz--;
if(s[i]=='L'){
l--;
b[wz]=b[wz+1]*a[l]%m;
}
else if(s[i]=='R'){
r++;
b[wz]=b[wz+1]*a[r]%m;
}
}
for(int i=0;i<n;i++){
cout<<b[i]<<' ';
}cout<<endl;
}
<2>(CF Gym103660F)
这题其实是一样的问题,感觉做这些题的时候没有什么算法技巧,但又没有什么思路,怎么办呢,这是后来补题做出来的。
题解:
题意较复杂,给定k,n俩个整数,经过计算约分后相加得出的值。
使用过快速幂,不过这题会超,必须得用数学思想。
代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin>>t;
while(t--){
long long int n,k;
cin>>n>>k;
long long int ans=(1+n)*n/2;
while(k>=1&&n>0){
ans=ans-(1+n/2)*(n/2)/2;
k--;
n=n/2;
}
cout<<ans<<endl;
}
return 0;
}
三、总结
牛客周赛好像打完了,寒假也快结束了,应该只有一俩场了,这周学的算法都挺懵的,有时间还得都回溯一遍,之前的题目也去看了一下,部分题目可以补题了,说明还是有进步的嘛。下周还是dijkstra和DP吧,这俩个点都好难啊,dijkstra可以引申一下别的最短路径求法。