【USACO training】Chapter 1 入门

整理的算法模板合集: ACM模板


Section 1.1 介绍

在这里插入图片描述

USACO Training 综述

http://train.usaco.org/usacogate(USACO 97道题)   
USA Computing Olympiad 是美国高校的信息学测评网站,也是美国中学生的官方竞赛网站。   
美国著名在线题库,专门为信息学竞赛选手准备。   
全英文界面,但有非官方的中文翻译。推荐直接阅读英语原文,既准确可靠又可提高英语水平。   
网站的Training题目全面,每道题附有详细题解,可查看测试数据和运行结果,便于调试、发现错误并改正。采用章节递进的层次结构,由易到难,讲授知识、练习编程结合,题目必须依次完成,避免了只挑简单题做的行为。   

第一章较为简单,属于竞赛入门题。基本不涉及太多算法和技巧,请在一周内刷完本章习题,用来巩固基础,并开始下一章的练习。

Section 1.2 提交解决方案,任务类型,特殊问题

luoguP1200、P1201、P1202、P1203

1.2.1 AcWing 1339. 你的旅途由此开始(字符串模拟)

在这里插入图片描述
经 典

简单的字符串模拟

#include <cstdio>
#include <algorithm>
#include <iostream>
std :: string s1, s2;
const int mod = 47;

int main()
{
    std :: cin >> s1 >> s2;
    int mul1 = 1, mul2 = 1;
    for(int i = 0; i < s1.length(); ++ i)
        mul1 *= (s1[i] - 'A' + 1) % mod;
    for(int i = 0; i < s2.length(); ++ i)
        mul2 *= (s2[i] - 'A' + 1) % mod;

    if(mul1 % mod == mul2 % mod)puts("GO");
    else puts("STAY");
    return 0;
}

1.2.2 AcWing 1340. 贪婪的送礼者(模拟,map)

在这里插入图片描述
模拟,直接用map存名字和当前的钱数,每次送钱的时候,自己能剩下的就等于 money - (money / num) * num,注意如果num = 0需要特判一下。剩下的直接模拟就行了

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>

using namespace std;
const int N = 50007;

int n, m;
map<string, int>mp;
string names[N];
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i){
        string s;
        cin >> s;
        names[i] = s;
        mp[s] = 0;
    }
    for(int i = 1; i <= n; ++ i){
        int money, num;
        string s;
        cin >> s;
        scanf("%d%d", &money, &num);
        int remain = 0;
        if(num != 0)remain = money - (money / num) * num;
        
        money -= remain;
        mp[s] -= money;
        for(int j = 1; j <= num; ++ j){
            string name;
            cin >> name;
            mp[name] += money / num;
        }
    }
    for(int i = 1; i <= n; ++ i){
        cout << names[i] << " " << mp[names[i]] << endl;
    }
    return 0;
}

1.2.3 AcWing 1341. 十三号星期五(日期模拟)

在这里插入图片描述
经典日期模拟题目。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

int month[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int weekday[7];

int main()
{
    int n;
    cin >> n;

    int year = 1900, days = 0;
    while (n -- )
    {
        for (int i = 1; i <= 12; i ++ )
        {
            weekday[(days + 12) % 7] ++ ;
            days += month[i];
            if (i == 2)
                if (year % 4 == 0 && year % 100 || year % 400 == 0)
                    days ++ ;
        }
        year ++ ;
    }

    for (int i = 5, j = 0; j < 7; i ++, j ++ )
        cout << weekday[i % 7] << ' ';

    return 0;
}

基姆拉尔森计算公式:

w e e k = ( d a y + 2 × m o n t h + [ 3 × ( m o n t h + 1 ) ÷ 5 ] + y e a r + [ y e a r ÷ 4 ] − [ y e a r ÷ 100 ] + [ y e a r ÷ 400 ] + 1 ) m o d 7 week=(day+2×month+[3×(month+1)÷5]+year+[year÷4]−[year÷100]+[year÷400]+1)mod7 week=(day+2×month+[3×(month+1)÷5]+year+[year÷4][year÷100]+[year÷400]+1)mod7

可以直接计算出某年某月某日是星期几(好像没什么用hhh)

#include <iostream>	
#include <cstdio>
#include <cstring>

using namespace std;	
const int N = 507;

int count[N];
int week_day(int year, int month, int day)	
{
    if (month == 1 || month == 2) month += 12, year -- ;
    return (day + 2 * month + 3 * (month + 1) / 5 + year + year / 4 - year / 100 + year / 400 + 1) % 7;	
}

int main()
{
    int year_len = 0;
    cin >> year_len;
	
    for (int current_year = 1900; current_year < 1900 + year_len; current_year ++ )
        for (int current_month = 1; current_month <= 12; current_month ++ )
            count[week_day(current_year, current_month, 13)]++;
    cout << count[6] << " "<<count[0]<< " "<<count[1]<< " "<<count[2]<< " "<<count[3]<< " "<<count[4]<< " "<<count[5];	
    return 0;
}

AcWing 1342. 断开的项链 (模拟)

在这里插入图片描述
直接暴力 O ( n 2 ) O(n^2) O(n2)枚举判断即可,但是这里需要注意的是一个类似爆搜的思路,因为w可以作为r也可以作为b,所以我们搜的时候把它变成r或者b各搜一次,最后别忘了回溯。

主要是这个数据有毒:

输入:

77
rwrwrwrwrwrwrwrwrwrwrwrwbwrwbwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwr

输出

74
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>

using namespace std;

string s, tmp;
int n, m;
int res;
int len;

int slove(int x, string tmp)
{
    string s = tmp;
    int res = 2;
    int l = x, r = (x - 1 % len + len) % len;
    for(;(l + 1) % len != r && (s[l] == s[(l + 1) % len] || s[(l + 1) % len] == 'w'); l = (l + 1) % len){
        res ++ ;
        if(s[(l + 1) % len] == 'w') s[(l + 1) % len] = s[l]; 
    }
    for(;(r - 1 + len) % len != l && (s[r] == s[(r - 1 % len + len) % len] || s[(r - 1 % len + len) % len] == 'w'); r = (r - 1 % len + len) % len){
        res ++ ;
        if(s[(r - 1 % len + len) % len] == 'w') s[(r - 1 % len + len) % len] = s[r];
    }
    return res;
}

int main()
{
    cin >> n >> tmp;
    s = tmp;
    len = s.length();
    for(int i = 0; i < len; ++ i){
        res = max(res, slove(i, s));
        if(s[i] == 'w') {
            s[i] = 'r', res = max(res, slove(i, s));
            s[i] = 'b', res = max(res, slove(i, s));
            s[i] = 'w';
        }
    }
    cout << res << endl;
    return 0;
}

Section 1.3 完全搜索

luogu P1204、P1205、P1206、P1207

AcWing 1343. 挤牛奶(区间合并,贪心 + 模拟)

在这里插入图片描述
区间合并的经典模板题。

我们只需要按照左端点排序,然后按照题意模拟即可。

我们只需要比较右端点,维护一个连续区间的左右端点l和r,如果一个新区间的左端点和连续区间的右端点有交集(端点也算)就更新连续区间的右端点表示合并到一起,如果新区间和连续区间的右端点没有交集就更新一个连续区间的左右端点表示到达了一个新的连续区间,此时更新答案即可。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>

#define x first
#define y second

using namespace std;

typedef long long ll;
typedef pair<int, int> PII;

const int N = 50007;

int n, m;
PII a[N];

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i){
        scanf("%d%d", &a[i].x, &a[i].y);
    }
    
    sort(a + 1, a + 1 + n);
    int ans1 = 0, ans2 = 0;
    int x = a[1].x, y = a[1].y;
    for(int i = 2; i <= n; ++ i){
        if(a[i].x <= y)
            y = max(y, a[i].y);
        else {
            ans1 = max(ans1, y - x);
            ans2 = max(ans2, a[i].x - y);
            x = a[i].x;
            y = a[i].y;
        }
    }
    ans1 = max(ans1, y - x);
    cout << ans1 << " " << ans2 << endl;
    return 0;
}

AcWing 1344. 转换(经典旋转类大模拟)

在这里插入图片描述

我们给出的是一个 n ∗ n n*n nn的方阵,所以我们可以使用一个比较神奇的方法实现矩阵旋转:我们先把方阵沿着主对角线对称,然后在沿着中轴镜面对称,即可实现顺时针旋转90度的操作。

然后就是镜像翻转的操作,我们只需要从两头开始swap即可。

注:如果不是方阵的话,矩阵旋转只能使用乘上一个构造的矩阵的方法来实现,时间复杂度比较高,需要 O ( 2 n ) O(2^n) O(2n)的复杂度。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>

using namespace std;

const int N = 5007, M = 500007;

int n, m;

typedef vector<string> VS;

void mirror(VS & c)
{
    for(int i = 0; i < n; ++ i) 
        for(int j = 0, k = n - 1; j < k ; ++ j, -- k) 
            swap(c[i][j], c[i][k]);
}

void rotate(VS& c)
{

    for(int i = 0; i < n; ++ i) 
        for(int j = 0; j < i; ++ j) 
            swap(c[i][j], c[j][i]);
    mirror(c);
}

int solve(VS a, VS b)
{
    VS c = a;
    for(int i = 1; i <= 3; ++ i) {
        rotate(c);
        if(c == b) return i;
    }
    c = a;
    mirror(c);
    if(c == b) return 4;
    for(int i = 1; i <= 3; ++ i) {
        rotate(c);
        if(c == b) return 5;
    }
    if(a == b) return 6;
    return 7;
}

string ch;

int main()
{
    VS a, b;
    cin >> n;
    for(int i = 1; i <= n; ++ i) 
        cin >> ch, a.push_back(ch);
    for(int i = 1; i <= n; ++ i)
        cin >> ch, b.push_back(ch);
    printf("%d\n", solve(a, b));
    return 0;
}

AcWing 1345. 序号命名(简单模拟)

在这里插入图片描述
直接模拟即可。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>

using namespace std;

const int N = 50007;

int n, m;
string num, cur;

int Get(char ch) {
    if(ch <= 'O') return (ch - 'A') / 3 + 2;
    if(ch <= 'S') return 7;
    if(ch <= 'V') return 8;
    return 9;
}

int main()
{
    int cnt = 0;
    cin >> num;
    while (cin >> cur) {
        string tmp = "";
        for (auto c : cur) 
            tmp += Get(c) + '0';
        if(tmp == num) {
            cnt ++ ;
            cout << cur << endl;
        }

    }
    if(cnt == 0) 
        puts("NONE");

    return 0;
}

AcWing 1346. 回文平方(回文 + 经典进制转换)

在这里插入图片描述
进制转换,使用短除法实现。

注意B是1~20,十进制以上要用字母表示。

判断是否为回文字符串,可以使用双指针或者直接 reverse

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>

using namespace std;

const int N = 50007;

int n, m;
string s;
int B;
/*
bool check(string s) {
    for (int i = 0, j = s.length() - 1; i< j ; ++ i, -- j) {
        if(s[i] != s[j]) return false;
    }
    return true;
}
*/
bool check(string s) {
    string ss = s;
    reverse(s.begin(), s.end());
    return ss == s;
}

char get(int x)//注意B可能是10进制以上, 要用ABCD表示
{
    if(x <= 9) return x + '0';
    return x - 10 + 'A';
}

string base(int x, int B)
{
    string res;
    while(x) {
        res += get(x % B);
        x /= B;
    }
    reverse (res.begin(), res.end());
    return res;
}

int main()
{
    cin >> B;
    for (int i = 1; i <= 300; ++ i) {
        string num = base(i * i, B);
        if (check(num)) {
            cout << base(i, B) << " " << num << endl;
        }
    }
}

AcWing 1347. 双重回文(暴力,进制转换)

在这里插入图片描述
直接暴力判断即可。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>

using namespace std;

const int N = 50007;

int n, m, s;

bool check(string s)
{
    string res = s;
    reverse(s.begin(), s.end());
    return res == s;
}

string base(int x, int B) {
    string res;
    while (x) {
        res += x % B;
        x /= B;
    } 
    reverse(res.begin(), res.end());
    return res;
}

int main()
{
    cin >> n >> s;
    
    for (int i = s + 1, num = 0; num < n; ++ i) {
        int cnt = 0;
        for(int j = 2; j <= 10 && cnt < 2; ++ j)
            if(check(base(i, j))) {
                cnt ++ ;
            }
        if(cnt == 2) {
            cout << i << endl;
            num ++ ;
        }
    }
    return 0;
}

Section 1.4 贪心,制定解决方案

luogu P1208、P1209、P1211、P1444、P3650、P2693

AcWing 1348. 搭配牛奶(简单贪心)

在这里插入图片描述
如果每个农民的牛奶必须取完的话,本题就是一个基础的01背包。
这里因为我们可以去一部分,所以是一个非常简单的贪心。

我们只需要按照价格排序,然后尽量买最便宜的,正确性显然。

#include <cstdio>
#include <algorithm>
#include <iostream>

using namespace std;

const int N = 50007;
int n, m;

struct Milk
{
    int p, a;
    bool operator < (const Milk& t) const {
        return p < t.p;
    }
}milk[N];

int main()
{
    scanf("%d%d", &m, &n);
    for (int i = 1; i <= n; ++ i) {
        scanf("%d%d", &milk[i].p, &milk[i].a);
    }
    
    sort(milk + 1, milk + 1 + n);
    int res = 0, cnt = 0;
    for (int i = 1; i <= n && cnt < m; ++ i) {
        int tmp = min(m - cnt, milk[i].a);
        res += milk[i].p * tmp;
        cnt += tmp;
    }
    cout << res << endl;
    return 0;
}

AcWing 1349. 修理牛棚(贪心 + 逆向思维 / 补集思想)

在这里插入图片描述

我们直接做的话不好做,直接贪心无法保证正确性。
我们需要使用到补集的思想,就是我们发现直接做不是很好做,我们可以使用逆向思维,
我们先用一块木板覆盖出全部,我们发现任何一种用m块木板覆盖的方案都可以用一块木板覆盖出全部,
然后中间选择间隔断开的方法转换过来,我们挑选m-1个地方断开即可。
为了让剩下的木板最小,就是让断开的木板距离最大,也就是说我们从大到小选择间隔,这样保证最优解。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 5007;

int n, m;
int a[N], b[N];
int s, c;

int main()
{
    scanf("%d%d%d", &m, &s, &c);
    for (int i = 0; i < c; ++ i) scanf("%d", &a[i]);
    sort(a, a + c);
    for (int i = 1; i < c; ++ i) b[i] = a[i] - a[i - 1] - 1;//3 - 1 - 1
    
    sort(b + 1, b + c, greater<int>());
    
    int res = a[c - 1] - a[0] + 1;//3 - 1 + 1
    
    for (int i = 1; i <= m - 1 && i < c; ++ i) {
        res -= b[i];
    }
    printf("%d\n", res);
    return 0;
}

AcWing 1350. 密码运算(模拟)

在这里插入图片描述
直接模拟即可。
注意所有的数都要判断是否是给定数组里的数。

//100~999,10~99
//check_len()位数是否合法
//check_in()是否出现的所有数字都是给定的数字

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
const int N = 5007;
int n, m;
int ai[N];

bool check_len(int x, int k) {
    return to_string(x).size() == k;
}

bool check_in(int x) {
    while (x) {
        int tmp = x % 10;
        x /= 10;
        bool flag = 0;
        for (int i = 1; i <= n; ++ i) {
            if (tmp == ai[i])
                flag = 1;
        }
        if (flag == 0)
            return false;
    } 
    return true;
}

bool check(int a, int b) {
    int c = a * (b % 10), d = a * (b / 10), e =  a * b;
    if (!check_in(a) || !check_in(b) || !check_in(c) || !check_in(d) || !check_in(e)) 
        return false;
    
    if (!check_len(c, 3) || !check_len(d, 3) || !check_len(e, 4)) 
        return false;
    return true;
}

int ans;

int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++ i) {
        cin >> ai[i];
    }
    for (int i = 100; i <= 999; ++ i) {
        for (int j = 10; j <= 99; ++ j) {
            if (check(i, j)) {
                ans ++ ;
            }
        }
    }
    cout << ans << endl;
    return 0;
}

AcWing 1351. 密码锁(模拟 / 容斥原理)

AcWing 1352. 虫洞

AcWing 1353. 滑雪场设计

Section 1.5 更多搜索技巧

luogu P1214、P1215

AcWing 1354. 等差数列

AcWing 1355. 母亲的牛奶

Section 1.6 二进制数

luogu P1216、P1217、P1218

AcWing 898. 数字三角形

AcWing 1356. 回文质数

AcWing 1357. 优质牛肋骨

已标记关键词 清除标记
课程简介: 历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring Boot、Spring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:设计师小姐姐 返回首页