2024年华北水利水电大学第六届ACM-ICPC程序设计大赛校赛-正式赛

目录

1 男孩和女孩

2 原神的大小数

3 第k小整数

4 抽奖活动

5 排队买延津火烧

6 打击犯罪

7 孤岛问题

8 农田灌溉问题

9 围棋数气

10 兔丁兴旺的兔子家族

11 梯度下降

总结:


1 男孩和女孩

最近这段时间有很多男孩子使用漂亮女孩子的头像在社交软件上聊天,这让聊天软件上的人区分屏幕对面的人的性别这件事变得很困难。
最近耗子哥在网上和一个使用美女头像的人聊的很开心,但是却不知道对面是男是女。耗子哥很害羞,他不敢问对方要照片或者打视频电话,他只能通过对方的网络昵称来判断对方是男是女,如果昵称中不同字符的数目是奇数,那么对方就是男性,否则是女性,如果对方是男性,那么将不符合耗子哥的心理预期,输出” IGNORE HIM!” ,让耗子哥忽略他,寻找下一个目标,否则输出” CHAT WITH HER!”
同时耗子哥会很高兴,我们希望耗子哥高兴,但是不希望你骗他,所以,如果欺骗耗子哥的感情是不能通过题目的。

输入格式:

一串字符串,字符数目不超过100,代表与耗子哥聊天人的网络昵称,字符仅包含小写字母

输出格式:

如果对方是男生,输出IGNORE HIM!
如果对方是女生,输出CHAT WITH HER!

输入样例:

abc

输出样例:

IGNORE HIM!

answer:

这个题目就是统计一下共有多少种不同字符,使用map统计一下即可,也可以用数组模拟。

#include<bits/stdc++.h>
typedef unsigned long long ull;
typedef long long ll;
using namespace std;
const int N = 1e5 + 10;
const int M = 2e5 + 10;
const int mod  = 1e9 + 7;
void solve(){
    string s;
    cin>>s;
    map<char,int> mp;
    for(int i = 0; i < s.size() ; i++){
        mp[s[i]]++;
    }
    if(mp.size() % 2 != 0){
        cout<<"IGNORE HIM!";
    }else{
        cout<<"CHAT WITH HER!";
    }
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL),cout.tie(NULL);
    int t = 1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0 ;
}

 

2 原神的大小数

原神是acm的大佬,所以他比较两个数大小的方式也和其他人的方式不太一样。他是通过比较两个十进制数转换成二进制数后,其中1的个数的多少来判断的。比如:5的2进制表示为101,其中有两个1。而8的2进制表示为1000,其中只有一个1,因此我们认定5比8大。但是转换工作太麻烦了。现在他想把这个任务交给聪明的你。

输入格式:

请在这里写输入格式。例如:输入在一行中给出2个不超过1000且大于等于0的整数A和B。

输出格式:

请你输出较大的那个数。(如果大小相同则输出10进制中较大的那个)

输入样例:

在这里给出一组输入。例如:

5 8

输出样例:

在这里给出相应的输出。例如:

5

answer:

这个题目考察的就是十进制数转二进制,转化过程中有1就加一下,最后注意判断即可。

#include<bits/stdc++.h>
typedef unsigned long long ull;
typedef long long ll;
using namespace std;
const int N = 1e5 + 10;
const int M = 2e5 + 10;
const int mod  = 1e9 + 7;
void solve(){
    int a,b;
    cin>>a>>b;
    int t1 = a,t2 = b;
    int num1 = 0,num2 = 0;
    while (a){        //十进制数转二进制,统计1的个数
        if(a%2 == 1){
            num1++;
        }
        a /= 2;
    }
    while (b){    
        if(b%2 == 1){
            num2++;
        }
        b /= 2;
    }
    if(num1 > num2){
        cout<<t1;
    }else if(num1 < num2){
        cout<<t2;
    } else{
        if(a > b){
            cout<<t1;
        } else{
            cout<<t2;
        }
    }
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL),cout.tie(NULL);
    int t = 1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0 ;
}

3 第k小整数

杨老师想要在现有 n 个正整数(这些正整数均小于 30000,且n≤10000)中,找到第k个最小的整数,k≤1000。

输入格式:

第一行是全部的 n个正整数和 k数值,第二行开始为 n 个正整数的值,整数间用空格隔开。

输出格式:

输出第 k 个最小整数的值;若无解,则输出“NO RESULT”。

输入样例:

在这里给出一组输入。例如:

10  3
37  6  44  40  52  73  94  82  89  63

输出样例:

在这里给出相应的输出。例如:

40

answer:

这个题目,在比赛中wa的比较多,原因就是没有考虑重复数(题目误导),解法就是可以使用map统计,最后输出map中第k个就行,或者可以使用set(可以自行搜索,我不会)。

#include<bits/stdc++.h>
typedef unsigned long long ull;
typedef long long ll;
using namespace std;
const int N = 1e5 + 10;
const int M = 2e5 + 10;
const int mod  = 1e9 + 7;
void solve(){
    int n,k;
    cin>>n>>k;
    vector<int> a(n+1,0);
    map<int,int> mp;
    for (int i = 1; i <= n; ++i) {
        cin>>a[i];
        mp[a[i]]++;
    }
    
    if(k > mp.size() || k == 0){    //无解的情况
        cout<<"NO RESULT"<<endl;
    } else{
        int cnt = 0;
        for (auto i : mp) {
            cnt++;
            if(cnt == k){
                cout<<i.first<<endl;
            }
        }
    }
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL),cout.tie(NULL);
    int t = 1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0 ;
}

4 抽奖活动

某公司年会组织了一个抽奖活动,具体要求是这样的:

首先,所有参见活动的人都将一张写有自己名字的字条放入抽奖箱中;

然后,待所有字条加入完毕,每人从箱中取一个字条;

最后,如果取得的字条上写的就是自己的名字,那么恭喜你,中奖了。

请你计算一下,若按这种抽奖方法,没有一个人中奖的概率是多少?

输入格式:

输入数据的第一行是一个整数m,表示测试实例的个数,然后是每行数据,每行包含一个整数n(1<n<=20),表示参加抽奖的人数。

输出格式:

对于每个测试实例,请输出没有一个人中奖的概率(用百分比表示),每个实例输出一行,结果保留两位小数(四舍五入)。

输入样例:

在这里给出一组输入。例如:

3
2
4
6

输出样例:

在这里给出相应的输出。例如:

50.00%
37.50%
36.81%

answer:

赛时想了一下,但没推出来,赛后补题,才知道这题考察的是“错排”,错排的意思就是n人去选n种物品(每个人都有唯一的一个正确物品对应),每个人选的都是错误物品的选择方法。错排的求法是D[n] = (n - 1) * ( D[n - 1] + D[n - 2] ),采用递归的求法,对于错排的详细解释,可以参考错排的详细解释。所以对于这道题,我们只需求出错排的所有可能的方法去除以所有排列的可能,也就是除以n!。

#include<bits/stdc++.h>
typedef unsigned long long ull;
typedef long long ll;
using namespace std;
const int N = 1e3 + 10;
const int M = 2e5 + 10;
const int mod  = 1e9 + 7;

ll d(ll n){     //计算错排的总个数
    if(n == 1) return 0;
    if(n == 2) return 1;
    return ( n - 1 )*(d( n - 1 ) + d( n - 2 ));
}

ll jc(ll x){    //计算阶乘,n!就代表所有的抽取个数
    if(x == 1) return 1;
    return x * jc( x - 1);
}

void solve(){
    int n;
    cin>>n;
    double ans = d(n)*1.0/jc(n) * 100;
    printf("%.2lf",ans);
    printf("\%\n");
}
signed main(){
    /*ios::sync_with_stdio(false);
    cin.tie(NULL);*/
    int t = 1;
    cin>>t;
    while(t--){
        solve();
    }
    return 0 ;
}

5 排队买延津火烧

延津县的大肉火烧很有名,每个从哪里过得游客或者当地人都很喜欢吃,但火烧是现烤的,加工时间有点长,所以火烧店每天都排很长的队。有n个人在排队等着买火烧,假如每个人买的火烧个数是Ti,请编程找出这n个人排队的一种顺序, 使得n个人的平均等待时间最小。(注意:等待自己火烧的加工时间也算等待时间)

输入格式:

输入文件共两行,第一行为n;第二行分别表示第 1 个人到第n个人每人要买的火烧个数(不超过1000)T1,T2,…,Tn,每 个数据之间有 1 个空格。

输出格式:

输出文件有两行,第一行为一种排队顺序,即1到n 的一种排列;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。

输入样例:

在这里给出一组输入。例如:

10
56 12 1 99 1000 234 33 55 99 812

输出样例:

在这里给出相应的输出。例如:

3 2 7 8 1 4 9 6 10 5
532.00

answer:

这个题目,相信写了的人都很“迷惑”,这不就是排个序,求一下前缀和的前缀和吗,数据也不大,但就是不对,经过在赛后的不断尝试,这题只有使用选择排序的排序方法才可以通过,非常奇怪(有可能数据造错了),这样就很容易通过了。还有一点就是计算前缀和的前缀和,我们可以通过计算每个元素的出现次数来计算,第一个元素出现了n次,第二个元素出现了n-1次,第n个元素出现了n - i + 1次(我的i是从1开始算起)。

#include<bits/stdc++.h>
typedef unsigned long long ull;
typedef long long ll;
using namespace std;
const int N = 1e5 + 10;
const int M = 2e5 + 10;
const int mod  = 1e9 + 7;
void solve(){
    int n;
    cin>>n;
    vector<pair<int,int>> a(n+1);
    ll sum = 0;
    double avg = 0;
    for(int i = 1; i <= n;i++){
        cin>>a[i].first;
        a[i].second = i;
    }
    for(int i = 1; i <= n-1;i++){    //选择排序
        for (int j = i + 1; j <= n; ++j) {
            if(a[i].first > a[j].first){
                swap(a[i],a[j]);
            }
        }
    }
    for(int i = 1; i <= n;i++){    //计算前缀和的前缀和
        sum += a[i].first*(n - i + 1);
    }
    avg = sum*1.0/n;
    for (int i = 1; i <= n; ++i) {
        cout<<a[i].second;
        if(i != n){
            cout<<" ";
        }
    }
    printf("\n%.2f" , avg);
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    int t = 1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0 ;
}

6 打击犯罪

某个地区有 n(n<=1000)个犯罪团伙,当地警方按照他们的危险程度由高到低给他们编号为 1-n,他们有些团伙之间有直接联系,但是任意两个团伙都可以通过直接或间接的方式联系,这样这里就形成了一个庞大的犯罪集团,犯罪集团的危险程度由集团内的犯罪团伙数量唯一确定,而与单个犯罪团伙的危险程度无关(该犯罪集团的危险程度为 n)。现在当地警方希望花尽量少的时间(即打击掉尽量少的团伙),使得庞大的犯罪集团分离成若干个较小的集团,并且他们中最大的一个的危险程度不超过 n/2。为达到最好的效果,他们将按顺序打击掉编号 1 到 k 的犯罪团伙,请编程求出 k 的最小值。

输入格式:

第一行一个正整数 n。接下来的 n 行每行有若干个正整数,第一个整数表示该行除第一个外还有多少个整数,若第 i 行存在正整数 k,表示 i,k 两个团伙可以直接联系。

输出格式:

一个正整数,为 k 的最小值

输入样例:

在这里给出一组输入。例如:

7 
2 2 5 
3 1 3 4 
2 2 4 
2 2 3 
3 1 6 7 
2 5 7 
2 5 6

输出样例:

在这里给出相应的输出。例如:

1

answer:

赛时时间没来的及,没看,以为很难(其实也不简单),参考:大佬讲解。其实就是并查集(比赛前我还看了两眼),题目要求就是,求出最少删除多少个点能使得每个连通分量的点数都小于n/2,大佬的思路就是,由于题目中要求从1到k顺序删除,那我们可以从第n个点开始添加,每添加一个点k,我们看合并后k所在的连通分量(集合)顶点数大于n/2,如果大于,那么k就是答案。而如何优雅的合并点,就需要用到并查集了,对于并查集,大家可以自行学习。

#include<bits/stdc++.h>
typedef unsigned long long ull;
typedef long long ll;
using namespace std;
const int N = 1e3 + 10;
const int M = 2e5 + 10;
const int mod  = 1e9 + 7;
int fa[N],cnt[N];

void intf(int n){    //初始化
    for (int i = 1; i <= n; ++i) {
        fa[i] = i;
        cnt[i] = 1;    //用于统计当前连通块点的个数
    }
}

int find(int x){    //寻找祖先
    if(fa[x] != x) return find(fa[x]);
    return x;
}

void  merge(int a,int b){    //合并
    int x = find(a);    //找到a的祖先
    int y = find(b);    //找到b的祖先
    if(x != y){
        cnt[y] += cnt[x];    //如果当前点没在,就合并长度
        fa[x] = y;
    }
}
vector<vector<int>> g(N);
void solve(){
    int n,m;
    cin>>n;
    for (int i = 1; i <= n; ++i) {
        cin>>m;
        for (int j = 1; j <= m; ++j) {
            int u;
            cin>>u;
            g[i].push_back(u);    //记录每个点的邻接点
        }
    }
    intf(n);
    for (int i = n; i >= 1; --i) {
        for (auto j : g[i]) {
            if(j > i){    //只合并当前大于i的点(从n - 1)
                merge(i,j);
            }
            if(cnt[find(i)] > n/2){    //不满足就输出k
                cout<<i;
                return;
            }
        }
    }
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    int t = 1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0 ;
}

7 孤岛问题

在R*C的矩阵地图上,每个坐标点位有一个小岛。这些小岛有些有供水管道连通,有些没有。

假设小岛之间供水管道连接只有横向和纵向两种类型,且有供水管道连通的小岛间物理距离满足曼哈顿距离。例如岛A和岛B是有供水管道连通的小岛,岛A坐标(a1​,a2​),岛B坐标(b1​,b2​),则两个岛之间距离满足|a1​-b1​|+|a2​-b2​|=1。

作为城市供水管理者的你,承诺居民不会存在有供水管道不连通的孤立岛屿。这需要解决矩阵地图上所有岛屿的管网连通问题。假设横向类型供水管路建设费用为2个华水币,纵向类型供水管路建设费用为1个华水币。

问题是:至少需要花费多少华水币才能使得矩阵地图中所有岛屿都有供水管道连通?

输入格式:

第一行输入矩阵地图两个正整数维度r和c。(0<r,c<=1000)

后面是有供水管道连通的所有小岛的坐标,每一行表示相连通的两个小岛在矩阵地图上的坐标(地图左上角小岛坐标为(1,1))。例如连通的岛A(a1​,a2​)和岛B(b1​,b2​),数据格式为a1​ a2​ b1​ b2​。

输出格式:

输出是能够让矩阵地图上所有小岛有供水管道连通的管路建设最小花费(单位:华水币)。

输入样例:

在这里给出一组输入。例如:

3 2
1 1 1 2
1 1 2 1
2 2 1 2
2 2 2 1

输出样例:

在这里给出相应的输出。例如:

2

answer:

eee,思路与代码来自于蔡光佬,对于解法的话,我还有点懵,过两天,脑子清楚了再来补充。

#include<bits/stdc++.h>
typedef unsigned long long ull;
typedef long long ll;
using namespace std;
const int N = 1e3 + 10;
const int M = 2e6 + 10;
const int mod  = 1e9 + 7;

int dx[4] = {0,1,0,-1};
int dy[4] = {1,0,-1,0};
int dw[4] = {2,1,2,1};

int fa[N*N],g[N][N];
int n,m,cnt = 0;
struct box{
    int a,b,dist;
}edge[M];

int find(int x){
    if(x != fa[x]) return find(fa[x]);
    return fa[x];
}

void intfa(){
    for (int i = 1; i <= n*m; ++i) {
        fa[i] = i;
    }
}
int get(int x,int y){
    return (x - 1) * m + y;
}
void solve(){
    cin>>n>>m;
    map<int,int> mp;
    intfa();
    int a1,a2,b1,b2;
    while (cin>>a1>>a2>>b1>>b2){
        if(a2 != b2){
            mp[min(a2,b2)] = 1;
        }
        int a = get(a1,a2);
        int b = get(b1,b2);
        fa[find(a)] = fa[find(b)];
    }
    int ans = 0;
    for (int i = 1; i < m; ++i) {
        if(!mp[i]){
            ans += 2;
            int a = get(1,i);
            int b = get(1,i + 1);
            fa[find(a)] = fa[find(b)];
        }
    }
    mp.clear();
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            mp[find(get(i,j))] = 1;
        }
    }
    ans += mp.size() - 1;
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    int t = 1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0 ;
}

8 农田灌溉问题

某乡村计划建造一条由东向西的农田灌溉沟渠。该沟渠要穿过一个有n 口井的水源。从每口井都要有一条沟渠沿最短路径(或南或北)与主渠相连。如果给定n口井的位置,即它们的x 坐标(东西向)和y 坐标(南北向),应如何确定主渠的最优位置,即使各井到主渠之间的沟渠长度总和最小的位置?证明可在规定时间内确定主渠的最优位置。

输入格式:

第1 行是井数n,1≤n≤10000。接下来n 行是井的位置,每行2个整数x和y,-10000≤x,y≤10000。

输出格式:

第1 行中的数是井到主渠之间的沟渠最小长度总和。

输入样例:

在这里给出一组输入。例如:

5 
1 2 
2 2 
1 3 
3 -2 
3 3

输出样例:

在这里给出相应的输出。例如:

 6

answer:

这题赛时,随便想了下,就过了,赛后看了下其实就是求中位数,到中位数的距离和是最优的。

#include<bits/stdc++.h>
typedef unsigned long long ull;
typedef long long ll;
using namespace std;
const int N = 1e5 + 10;
const int M = 2e5 + 10;
const int mod  = 1e9 + 7;
void solve(){
    int n;
    cin>>n;
    vector<int> a(n+1,0);
    for (int i = 1; i <= n; ++i) {
        int x;
        cin>>x;
        cin>>a[i];
    }
    if(n == 1){
        cout<<"0";
        return;
    }
    sort(a.begin()+1,a.end());
    int mid = 0;
    if(n % 2 != 0){
        mid = n/2 + 1;
    } else{
        mid = n/2;
    }
    ll sum = 0;
    for (int i = 1; i <= n; ++i) {
        sum += abs(a[i] - a[mid]);
    }
    cout<<sum<<endl;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    int t = 1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0 ;
}

9 围棋数气

围棋的气是一个被对方围住的封闭区域,如下图中棋局,0表示没有棋子的位置,1表示有棋子的位置,红色区域所包括的空位置数就是气数。你要做的是在不同的棋局中,能够自动计算气数。棋盘大小不超过100*100。

输入格式:

围棋的气是一个被对方围住的封闭区域,如下图中棋局,0表示没有棋子的位置,1表示有棋子的位置,1包围的区域中0的个数就是气数。你要做的是在不同的棋局中,能够自动计算气数。

输出格式:

棋局中的气数。

输入样例:

在这里给出一组输入。例如:

0 0 0 0 1 1 0 0 0 0
0 0 0 1 0 1 0 0 0 0
0 0 1 0 0 1 0 0 0 0
0 1 0 0 0 1 0 0 0 0
1 0 0 1 1 1 0 0 0 0
0 1 1 0 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1
0 0 0 0 0 1 0 0 0 1
0 0 0 0 0 1 1 1 1 1

输出样例:

在这里给出相应的输出。例如:

11

answer:

这个题目赛时一发入魂,一看到就是典型的bfs,由于最近做bfs也比较多,就直接ac了,问了几个佬,他们都被输入全退了,输入确实有点难受,但输入处理完之后,就比较好解决了。题意就是找到所有被1完全包围的0的个数,刚开是想从1开始bfs周围的0,但一直不行,快结束了想到可以直接从四个边开始遍历,因为如果从四个边的0 bfs 到的0一定不会被1包围,所以我们可以开一个vt二维数组,为1的直接标记为1,接下来从四个边的0开始bfs,遍历到的都修改vt数组为1,最后统计0的个数即可。

#include<bits/stdc++.h>
typedef unsigned long long ull;
typedef long long ll;
using namespace std;
const int N = 1e3 + 10;
const int M = 2e5 + 10;
const int mod  = 1e9 + 7;
struct box{
    int x;
    int y;
};
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
int g[N][N];
int n = 0,m = 0,k = 1;
int vt[N][N];
void bfs(int x,int y){  //bfs模版,看不懂的话,自己去学bfs
    box tmp;
    tmp.x = x;
    tmp.y = y;
    queue<box> q;
    vt[x][y] = 1;
    q.push(tmp);
    while (!q.empty()){
        tmp = q.front();
        q.pop();
        for (int i = 0; i < 4; ++i) {
            int tx = tmp.x + dx[i];
            int ty = tmp.y + dy[i];
            //下一个元素为0且不越界
            if(tx >= 1 && tx <= n && ty >= 1 && ty <= m && vt[tx][ty] == 0 && g[tx][ty] == 0){
                vt[tx][ty] = 1; 
                q.push({tx,ty});
            }
        }
    }
}

void solve(){
    string s;
    while (getline(cin,s)){ //处理输入
        n++;
        k = 1;
        for (int i = 0; i < s.size(); ++i) {
            if(s[i] != ' '){
                g[n][k] = s[i] - '0';
                k++;
            }
        }
        m = k - 1;
    }
    //m和n一定要计算正确
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if(g[i][j] == 1){
                vt[i][j] = 1;   //现将vt中为1的标记为1
            }
        }
    }
    /**
     * 下面是从四个边分别开始bfs
     */
    for (int i = 1; i <= m; ++i) {
        if(g[1][i] == 0 && vt[1][i] == 0){
            bfs(1,i);
        }
    }
    for (int i = 1; i <= m; ++i) {
        if(g[n][i] == 0 && vt[n][i] == 0){
            bfs(n,i);
        }
    }
    for (int i = 1; i <= n; ++i) {
        if(g[i][1] == 0 && vt[i][1] == 0){
            bfs(i,1);
        }
    }
    for (int i = 1; i <= n; ++i) {
        if(g[i][m] == 0 && vt[i][m] == 0){
            bfs(i,m);
        }
    }
    /*
     * 最后统计0的个数即可
     */
    int ans = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if(vt[i][j] == 0){
                ans++;
            }
        }
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    int t = 1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0 ;
}

10 兔丁兴旺的兔子家族

科学家研究发现,某星球兔子的不会随着时间流逝而减少数量,且每年数量和年份数有一定关系。从元年开始计算,第n年星球上增加的兔子数量为n的因子的个数,例如在第8年星球上新增兔子数量为4。
若年份数为n,新增兔子数量表示成一个函数f(n),则有如下表格数据:

n12345678
f(n)12232424

科学家希望了解到第n年的时候,星球上兔子总共的数量,即希望统计一个数值S
S=Σi=1i=n​f(i)

输入格式:

一个整数n(0 < n < 1000000)。

输出格式:

一个输出,为整数S,即为第n年,星球上兔子的总数。

输入样例:

在这里给出一组输入。例如:

4

输出样例:

在这里给出相应的输出。例如:

8

answer:

这个题其实就是求1 - n每个数因子的总个数,赛时使用求因子的方法写了一下,毫无疑问超时,赛后才明白可以直接求1 - n每个元素的倍数个数,这两个问题是等价的。1的倍数有n/1个,2的倍数有n/2个,3的倍数有n/3个,n的倍数有n/n个,最后统计sum即可。

#include<bits/stdc++.h>
typedef unsigned long long ull;
typedef long long ll;
using namespace std;
const int N = 1e5 + 10;
const int M = 2e5 + 10;
const int mod  = 1e9 + 7;
void solve(){
    int n;
    cin>>n;
    ll ans = 0;
    for (int i = 1; i <= n; ++i) {
        ans += n/i;
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    int t = 1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0 ;
}

11 梯度下降

梯度下降是机器学习中的常用算法,通过不断迭代计算函数的梯度,判断该点的某一方向和目标之间的距离,最终求得最小的损失函数和相关参数,为建立线性模型提供支持。
 

a67cfbdc-4746-4646-a135-88ee94bed7ee.png


耗子哥现在需要设计一个梯度下降算法,这个算法能够保证整个下降路线所经历的节点数最多。如下面这个例子:

12345
161718196
152425207
142322218
131211109

在这个矩阵中,每个点位可下降的方向有四个,下降路线上经历的矩阵节点数量称为下降长度。在这个例子中,一条可行的下降路线为25-24-17-16-1(从25开始到1结束),其下降长度为5;也可以是25-24……2-1(从25开始到1结束),其下降长度为25。两条路线中,后者是较长的一条。耗子哥希望能够找到所有下降路径中,长度最长的那一个。

输入格式:

输入的第一行为表示区域的二维数组的行数M和列数N(1≤M,N≤100),下面是M行,每行有N个数代表高度。

输出格式:

输出矩阵中最长下降路径的下降长度。

输入样例:

在这里给出一组输入。例如:

5 5 
1 2 3 4 5 
16 17 18 19 6 
15 24 25 20 7 
14 23 22 21 8
13 12 11 10 9

输出样例:

在这里给出相应的输出。例如:

25

answer:

这个题目也是一个挺难的问题,赛后发现这是一个经典的最长递增路径问题,解题方法就是dfs + 记忆化搜索,类似dp,我也尝试使用bfs写了一下,但要不就是答案错误,要不就是超时。关于记忆化搜索大家可以查看大佬讲解。总之,记忆化dfs的核心在于:勤俭节约。具体解释大家自己搜大佬的吧,我没那实力。

#include<bits/stdc++.h>
typedef unsigned long long ull;
typedef long long ll;
using namespace std;
const int N = 1e3 + 10;
const int M = 2e5 + 10;
const int mod  = 1e9 + 7;
int dx[4] = {0,0,-1,1};
int dy[4] = {1,-1,0,0};
int g[N][N];
int cnt[N][N];      //保存每个节点的最长路径
int n,m;
int ans = 0;
int dfs(int x,int y){
    if(cnt[x][y] != 0){
        return cnt[x][y];
    }
    int len = 1;
    for (int i = 0; i < 4; ++i) {
        int tx = x + dx[i];
        int ty = y + dy[i];
        if(tx >= 1 && tx <= n && ty >= 1 && ty <= m && g[tx][ty] > g[x][y]){
            len = max(len, dfs(tx,ty) + 1);
        }
    }
    cnt[x][y] = len;
    return len;
}
void solve(){
    cin>>n>>m;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            cin>>g[i][j];
        }
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            ans = max(ans,dfs(i,j));
        }
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    int t = 1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0 ;
}

总结:

这次校赛写的还是挺崩的,许多大佬都打崩了,我在最后30分钟侥幸过了2题,最终写了5题,排名还是比较靠前的(没有参考价值),这次校赛见到的也都是以前没见过的,也真的学到了很多。最后,感谢大家观看。

  • 22
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值