首页 > 其他分享 >CSP_J 暑假清北学堂集训 第二天

CSP_J 暑假清北学堂集训 第二天

时间:2023-07-11 18:44:20浏览次数:35  
标签:nowsum int max dfs 集训 清北 now CSP nownum

倍增算法:(只往上和) 
f[i][j] : 从ai 开始的2的j次方个数的最大值 
        = max(ai + ai+1 + ......+ ai+2^j-1) 
f[i][0] = ai
//切一刀:f[i][j] = max(f[i][j - 1] , f[i + 2^(j-1)][j - 1]) 
Q:一个区间内的最大值 n <= 100000
思路: 
l = 2, r = 5 f[2][2];
如果恰好是2的次方 抄答案 
else 
l = 2 r = 8  ->  4 <= 7 <= 8  ->  max(f[2][2], f[5][2])
int n,a[10000][10000];
int x[100000];x//代表长度为i的区间 用两个长度为2^x[i] 的区间能够覆盖 
f[100010][20];//j完全可以村吸收存下 
int main(){
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    x[1] = 0;
    for(int i = 2; i <= n; i++) x[i] = x[i >> 1] + 1;
    //x[111] = x[55] + 1
    for(int i = 1; i <= n; i++) f[i][0] = a[i];
    for(int j = 1;(1<<j) <= n; j++)
         for(int i = 1; i + (1<<j) - 1 <= n; i++)
    f[i][j] =max(f[i][j- 1], f[i + (1<<(j - 1))][j - 1])
    x[1] = 0;
    for(int i = 2; i <= n; i++) x[i] = x[i >> 1] + 1;
    //x[111] = x[55] + 1
    cin >> m;
    for(int i = 1; i <= m; i++){
        int l,r;
        cin >> l >> r;
        int len = r - l + 1;
        cout << max(f[l][x[len]], f[r - (1 << j) + 1][j] << '\n' ;
    }
} 
二分查找算法:(只往下砍) 
Q:给出序列a1 ~ an(单调不减)
m次访问, 给出x 求有没有出现x 
思路: 从中间砍一刀 , 左区间最大值为am, 如果x > am ,x在右半部分
如果小于am, 那么x一定出现在左区间,再砍一刀,继续比较……直到只剩一个数的时候停下来(x)
int main(){
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    sort(a + 1, a + n + 1);
    cin >> m;
    for(int i = 1; i <= m; i++){
        int x;
        cin >> x;
        int l = 0, r = n; //当前x 可能出现的范围 注意:l = 0 
        while(l + 1 != r){
            int m = (l + r) >> 1;
            if(x <= a[m]) r = m;//当前区间 从l + 1 ~ r变成 l + 1 ~ m
            else l = m;//当前答案在右边 
        }
        if(a[r] == x) cout << "Yes" << endl; //return true;
        else cout << "No" << endl; // return false;
    } 
}

 


注意:能够二分的问题数据需要具有单调性 
习题:
Q:https://ac.nowcoder.com/acm/problem/24866
思路:
什么时候t[i] 小于当前 b[i] ,当前放的就是 b[i] 首歌

前缀和:(单调递增)


int main(){
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> b[i];
    for(int i = 1; i <= n; i++) a[i] = a[i - 1] + b[i];
    sort(a + 1, a + n + 1);
    cin >> m; 
    for(int i = 1; i <= m; i++){
        int x;
        cin >> x;
        //找一个最小的aj 使得aj >= x 即可 
        int l = 0, r = n; 
        while(l + 1 != r){
            int m = (l + r) >> 1;
            if(x <= a[m]) r = m;
            else l = m;
        }
        cout << r << endl;
    } 
}
/*基本二分答案(写给自己看的,没题目链接):
找到一个最小的可实现的答案。*/
Code:
bool check(int t){// 检查能否在t分钟把所有衣服弄干 
    int sum = 0;
    for(int i = 1; i <= n; i++) if(a[i] > t)  sum += (a[i] - t - 1) / k + 1;//关键 
    if(sum <= t) return true;
    else return false;
}
int main(){
    cin >> n;
    for(int i = 1;i <= n; i++) cin >> a[i];
    sort(a + 1, a + n + 1);
    int l = 0, r = 1919810;
    while(l + 1 != r) {
        int m = (l + r) >> 1;
        if(check(m)) r = m;
        else l = m;
    }
    cout << r << endl;
} 

快速幂:
计算x的y次方(数论), 将幂次分成几次小运算 


int ksm(int x,int y,int p){//x^y%p
    if(y == 0) return 1;
    int z = ksm(x,y / 2,p);//z = x ^ (y / 2) % p;
    z = 1ll * z * z % p;// 1ll 为long long 类型的1 强制类型转化 
    if(y & 1) z = 1ll = z * x & p;
    return z; 
}

矩阵(二维数组):
用处:
1.加法:矩阵 + 矩阵 = 矩阵们的对应位置相加 ! 行和列都一样才可以进行 
2.减法:矩阵 + 矩阵 = 矩阵们的对应位置相减 ! 行和列都一样才可以进行 
3.乘法:A * B = C  
       ||  ||  ||
     n*m  m*k  n*m
	 i 行 j 列 = 去除第一个矩阵的第i行 乘 第二个矩阵的第j列 乘出的数相加
举例;
用处: 
1.可以用来做部分动态规划的加速 
2. 线性递推
斐波那契数列递推式: f[i] = f[i - 1] + f[i - 2]; 


struct matrix{//矩阵  
    int n,m;
    int a[5][5];
    matrix(){
        n = m = 0;
        memset(a,0,sizeof(a));
    }
};
matrix operator*(const matrix &m1, const matrix &m2){//m1 * m2 注意取地址, 避免重复拷贝值(当我们在使用函数的时候, 参数会拷贝一份) const :乘完了之后,不能改变m1 和 m2的值 
    matrix m3;
    m3.n = m1.n; m3.m = m2.m;
    for(int i = 1; i <= m3.n; i++)
        for(int j = 1; j <= m3.m; j++)
            for(int k =1; k <= m1.m; k++)//对应位置相乘 
                m3.a[i][j] += m1.a[i][k] * m2.a[k][j];//时间复杂度:n^3 所以极限为500或200 更大就一定不是用矩阵乘法完成 
    return m3; 
}
//由于用了 operator 可以用 m3 = m1 * m2 实现(优雅) 
matrix ksm(int x,int y){//矩阵快速幂 
    if(y == 0) {
        matrix z;
        z.n = z.m = x.n;
        for(int i = 1; i <= n; i++) 
            z.a[i][i] = 1;
        return z;
    }
    matrix z = ksm(x, y / 2, z);
    z = z * z;
    if(y & 1) z = z * x;
    return z;
}
第一个的列数等于第二行的行数 
矩阵乘法的性质:
1.结合律 a * b * c = a * (b * c)
2.没有交换律一说 a * b != b * a
3.分配率 a(b + c) = ab + ac
         (a + b)c = ac + bc

贪心:
Q:N个数, 求最大的前M个数
思路:
从大到小排序, 求前M个数的和即可
贪心题核心:排序 
Q:P1080
恰逢 H 国国庆,国王邀请 nn 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数
,国王自己也在左、右手上各写一个整数。然后,让这 nn 位大臣排成一排,国王站在队伍的最前面。排好队后,
所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的 
数的乘积除以他自己右手上的数,然后向下取整得到的结果。

国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大
臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

思路:
一个人左手很大,他就不应该排在前面;一个人的右手很小,他就不应排在后面 
先来比较一下两位大臣时的情况 总共两种情况
struct ren{
    int l , r;
}a[maxn];
bool cmp(ren x, ren y){//x是否应该站在前面 
    int v1 = max(a[0].l / x.r, a[0].l * x.l / y.r);// 国王 x y 
    int v2 = max(a[0].l / y.r, a[0].l * y.l / x.r);// 国王 y x  
    if(v1 < v2) return true;
    else return false;
}
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i].l >> a[i].r; 
//if(n == 2){
    //if(cmp(a[1],a[2]));
    //else swap(a[1], a[2]);
//}两个人时候的情况 
sort(a + 1, a + n + 1, cmp);//只要保证两个大臣, 这道题就做完了(其实还没有,万恶的高精请自己完成) 

/*但是我们并不需要两个人的数据, 那我们怎么搞呢?注意,这道贪心题的第一步是判断 
但是我们这段代码可以在cmp改进一下:
比较两个max,只取决于四个数的max是谁
等价于我们要在这四个数中找最大值 
由于 a[0].l / x.r < a[0].l * y.l / x.r 所以可以删掉
由于 a[0].l / y.r < a[0].l * x.l / y.r
剩下两个数, 我们经过约分, 可得 max(x.l * x.r, y.l * y.r)*/
 bool cmp(ren x, ren y){//x是否应该站在前面 
    int v1 = x.l * x.r;// 国王 x y 
    int v2 = y.l * y.r;// 国王 y x  
    if(v1 < v2) return true;
    else return false;
}
1.假设只有两个数据的时候,得出式子 
2.把写出来的式子进行排序 


t2:
山洞体积为V
搬运物品过程中要占据b[i]使用的体积的 
第i个物品要占据a[i] 的体积 能否把物品全部放下

思路 :
当然是贪心!(学的就是贪心) 
要决定搬物品的顺序 使用的体积的最小
当共有两件物品的时候:
先搬1:V = max(a1, a2 + a1, b1, b2 + a1)
先搬2:V = max(b1, b2, a2 + b1, a1 + a2) 
bool cmp(ren x, ren y){//x是否应该站在前面 
    int v1 = max(a1, a2 + a1, b1, b2 + a1)//完整代码请自行补充 
    int v2 = max(b1, b2, a2 + b1, a1 + a2) 
    if(v1 < v2) return true;
    else return false;
}
搜索:
基本形式:DFS、BFS 
Q:dfs 查找 前m个最大的数的和 (沿着一条路走到黑)


void dfs(int now, int nownum, int nowsum){//当前要看第now个元素选不选 已经选了nownum 以精选的书和是nowsum 
    if(now > n){
        if(nownum == m) ans = max(ans, nowsum);
        return ;
    }
    dfs(now + 1, nownum, nowsum)//不选
    dfs(now + 1, nownum + 1, nowsum + a[now]);//选
}
int main(){
    cin >> n >> m;
    for(int i = 1; i <= 1; i++)cin >> a[i];
    dfs(1, 0 , 0); 
    cout << ans << endl;
    return 0;
}

 bfs查找  一个网格里面有若干障碍物,求如何到终点
把起点表为零 把他周围的点标记为1 再往外走标记为2步 以此类推 如果到达终点就停止拓展 即答案 并且要保证每个状态只被走一次 
#include<bits/stdc++.h>
using namespace std;
int n,m,a[1005][1005];
int sx,sy;//起点
int tx,ty;//终点 
int step[1005][1005];
//step[i][j]代表从起点走到(i,j)需要走多少步 
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>a[i][j];//a[i][j]==1代表有障碍物
            
    cin>>sx>>sy>>tx>>ty;
    memset(step,-1,sizeof(-1));
    step[sx][sy]=0;
    
    /*
        struct pair{
            int first;
            int second;
        };
    */
    
    queue<pair<int,int> > q;//用来存能够向外进行拓展step的点
    q.push(make_pair(sx,sy)); 
    
    while(q.size()){
        int x=q.front().first;
        int y=q.front().second;
        q.pop();//当前的点为(x,y)
        
        for(int i=0;i<4;i++){//枚举上下左右四个方向 
            int xx=x+dx[i];
            int yy=y+dy[i];//从(x,y)走到了(xx,yy)
            
            if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&a[xx][yy]!=1&&step[xx][yy]==-1){
                step[xx][yy]=step[x][y]+1;
                q.push(make_pair(xx,yy));
            }
             
        }
    }
}

 

搜索问题分类:
1.最优解问题  有多个可行解求最优解 
2.可行解问题  找到一组符合条件的解 
3.解数量问题  有多少组解
什么样的问题用BFS:
1.保证该问题是最优解问题 
2.一定求的是最小步数(每操作一次的代价只有一)

什么样的问题用DFS: 剩余的所有情况都用DFS 

优化搜索的技巧:
1.剪枝 少走一些无用状态 */
Q1:
void dfs(int now, int nownum, int nowsum){//当前要看第now个元素选不选 已经选了nownum 以精选的书和是nowsum 
    //if(nownum > n) return;优化  可行性剪枝 大于范围 一定不合法 
    //if(nownum + n - now + 1 < m) return; 全加上都到不了范围 一定不合法
    //if(nowsum + sum[n] - sum[now] - 1 <= ans) return;都选上之后 还是没有能够超过ans的选择方案 所以break : 最优性剪枝 
    if(now > n){
        if(nownum == m) ans = max(ans, nowsum);
        return ;
    }
    dfs(now + 1, nownum, nowsum)//不选
    dfs(now + 1, nownum + 1, nowsum + a[now]);//选
}
int main(){
    cin >> n >> m;
    for(int i = 1; i <= 1; i++)cin >> a[i];
    dfs(1, 0 , 0); 
    cout << ans << endl;
    return 0;
}

 

应试优化技巧:卡时 由于搜索很容易超时 然而我们不会优化代码 所以我们卡个计时器 到最后一刻的时候停下搜索将当前最优解输出 因为超时一定是0分 但是我们用我们的ans可以 >= 0 分 好多了呀

 



#include <ctime>
#include <cstdlib>
void dfs(int now, int nownum, int nowsum){//当前要看第now个元素选不选 已经选了nownum 以精选的书和是nowsum 
    clock();//代表程序运行了多久 单位在win上是毫秒 在vin、洛谷 等是纳秒
    if(1000 * clock() > 950 / CLOCKS_PER_SEC){
        cout << ans << endl;
        exit(0);//卡时 
    } 
    if(now > n){
        if(nownum == m) ans = max(ans, nowsum);
        return ;
    }
    dfs(now + 1, nownum, nowsum)//不选
    dfs(now + 1, nownum + 1, nowsum + a[now]);//选
}
int main(){
    cin >> n >> m;
    for(int i = 1; i <= 1; i++)cin >> a[i];
    dfs(1, 0 , 0); 
    cout << ans << endl;
    return 0;
}
应试技巧:改变搜索顺序 
自拟顺序  
void dfs(int now, int nownum, int nowsum){//当前要看第now个元素选不选 已经选了nownum 以精选的书和是nowsum 
    if(now > n){
        if(nownum == m) ans = max(ans, nowsum);
        return ;
    }
    dfs(now + 1, nownum, nowsum)//不选
    dfs(now + 1, nownum + 1, nowsum + a[now]);//选
}
int main(){
    cin >> n >> m;
    for(int i = 1; i <= 1; i++)cin >> a[i];
    random_shuffle(a + 1, a + n + 1); 
    dfs(1, 0 , 0); 
    cout << ans << endl;
    return 0;
}

标签:nowsum,int,max,dfs,集训,清北,now,CSP,nownum
From: https://www.cnblogs.com/wan169961/p/17545642.html

相关文章

  • 202307 成都集训游记
    题单内容总结:20230708数据结构-金天Treasure-HDU7144Fightandupgrade-HDU7181长存不灭的过去、逐渐消逝的未来-LGP5067DataStructureQuiz-Baekjoon18756小球进洞-LOJ578DuffisMad–CF587FBreadboardCapacity-CF1368H2BearandBowling-CF......
  • CSP_J 暑假清北学堂集训
    图论:图的概念由点和边构成的元素边:如果边都有方向我们叫它有向图没方向叫无向图一、图的一些基本概念:1.度:一个顶点连了几条边就是它多少度2.有向图里的入度和出度:连向自己的度就是入度往外连得就是出度3.有向图里的自环:既是入度又是出度4.路径:只要沿着边走叫做路径如:1->2......
  • PMP®证书增持 CSPM-2证书,到这办理真便捷
    2023年6月起,持有PMP®证书的朋友可以直接增持一个同等级证书CSPM-2,不用重新考试,不用重新学习,原PMP®证书不影响正常使用,相当于多了一个国标项目管理领域的证书。 第一步·准备资料 1、填写能力评价表2、提供2张2寸蓝底彩照(电子版另外收10元打印费)3、提供PMP®证书电子版1份4、快......
  • 二中集训游寄
    Day0书接上回休业式,退役寄。upd:复活了。Day1(7.4)模拟赛,\(100+10+20+20=150\),总共\(45\)位巨佬,我\(10/46\),单调队列了\(35\)人,好耶!今天比较符合NOIP2022,老师说1=线120左右,我1=了?!今天没有数据结构,好耶!这次似乎不止QZ的,还有CX、YW的,有新高二,有新高一,有新初三......
  • CW暑假集训
    集训模拟赛的题解应该都在CWOI杂题里。主要就是题目的记录?不太想写游记。简单题不会写。7.7考试,考得依托。7.8很趣味的数据结构!感觉很有集训那味啊,就是前面讲一会简单的东西然后突然上强度。gym100739E.LifeasaMonster还是挺简单。套路地把切比雪夫距离转成曼哈顿......
  • 20230710巴蜀暑期集训测试总结
    T1打个不太暴的暴力但是爆了。只对了subtask1,不清楚发生了什么。先建出Kruscal重构树,对每个询问二分答案,判断就用暴力启发式合并T2打了一个\(20pts\)dp。第一步没有想到,每怎么见过这种题。将问题转化为满足\(\foralli,x_i\leA_i,x_i\leB_i\)的序列\(x\)个数。枚......
  • UOJ #37. [清华集训 2014] 主旋律
    UOJ传送门考虑dp。设\(f_S\)为点集\(S\)构成强连通分量的方案数。容易想到容斥。设\(ed_S\)为\(S\)内部连边数,那么\(f_S\)就是总的方案数\(2^{ed_S}\)减去构成的不是强连通分量的方案数。我们考虑如果整个图不是一个强连通分量,那么缩点后一定有\(>1\)个分量,并......
  • 暑假QBXT集训01
    Day1有向无环图一种特殊的有向图,没有任何环,简写为DAG。对于这种图,我们就有“拓扑序”。拓扑序不是唯一解。拓扑排序流程:每次删去一个没有入度的点加入拓扑序中,如此重复下去即可。P1807最长路题目描述:设\(G\)为有\(n\)个顶点的带权有向无环图,\(G\)中各个顶点的编......
  • CSP - J 训练营
    Day1数据结构含义:拿来存储数据的结构常见形式:1.变量只能存一个数。2.数组所有数组都开在全局变量。堆空间全局变量在堆空间。空间为$256M$,可以存$6.4×10^7$个int。栈空间局部变量在栈空间。空间为$64KB$,只能存$1.6×10^5$个int。......
  • Solution Set - 2023 省队集训
    2023-7-8模拟赛铁路(railway)Source:ROI2017D1T4C国有\(n\)个城市与\(m\)条铁路线,铁路均为单向,第\(i\)号铁路线被从起点到终点的\((s_i+1)\)个城市\(c_{i,1},c_{i,2},\cdots,c_{i,s_i+1}\)分为\(s\)段,从\(c_{i,j}\)乘铁路到\(c_{i,j+1}\)......