2022-6-2 森林中的兔子,最长有效括号串,小明和二分图,完全背包问题,数独,小陶与杠铃片

1. 森林中的兔子 - 贪心

森林中有未知数量的兔子。提问其中若干只兔子 “还有多少只兔子与你(指被提问的兔子)颜色相同?” ,将答案收集到一个整数数组 answers 中,其中 answers[i] 是第 i 只兔子的回答。

给你数组 answers ,返回森林中兔子的最少数量。

Example 1

输入:answers = [1,1,2]
输出:5
解释:
两只回答了 "1" 的兔子可能有相同的颜色,设为红色。 
之后回答了 "2" 的兔子不会是红色,否则他们的回答会相互矛盾。
设回答了 "2" 的兔子为蓝色。 
此外,森林中还应有另外 2 只蓝色兔子的回答没有包含在数组中。 
因此森林中兔子的最少数量是 5 只:3 只回答的和 2 只没有回答的。

Example 2

输入:answers = [10,10,10]
输出:11

Code

#include<bits/stdc++.h>

#pragma GCC optimize(2)
using namespace std;

int main() {
    ios::sync_with_stdio(false);

    unordered_map<int, int> mp;
    int n, t, ans = 0;
    cin >> n;
    while (n--) {
        cin >> t;
        ++mp[t];
    }
    
    for (auto& [x, y]:mp) {
        ans += (x + y) / (x + 1) * (x + 1);
    }
    cout << ans;

    return 0;
}

2. 最长有效括号串 - 栈

小明的老师给小明布置了一道作业,作业内容如下:

给定一个只包含 '('')' 的字符串,找出最长的包含有效括号的子串的长度。

由于小明太忙了,所以想请你帮忙完成,聪明的你可以帮小明解决这个问题吗?

有效括号串定义为左右可以匹配,例如'('')'可以匹配。

匹配规则如下:

(1)若a是有效括号串,则(a)也是;

(2)若ab是有效括号串,则ab也是。

Example 1

输入:)()())
输出:4

Example 2

输入:)(()())())(()())
输出:8

Code 1 [低效率代码]

#include<bits/stdc++.h>

#pragma GCC optimize(2)
using namespace std;

int main() {
    ios::sync_with_stdio(false);

    string s;
    cin >> s;

    stack<int> st;  // idx
    unordered_map<int, int> mp; // [end, cnt]
    int maxLength = 0, curLength, prevLength;
    for (int i = 0; i < s.size(); ++i) {
        if (s[i] == '(') {
            st.push(i);
        } else {  // s[i] == ')'
            if (!st.empty() && s[st.top()] == '(') {  // match
                curLength = i + 1 - st.top();
                prevLength = mp.count(st.top() - 1) ? mp[st.top() - 1] : 0;
                maxLength = max(maxLength, curLength + prevLength);
                mp[i] = curLength + prevLength;
                st.pop();
            } else {  // no match
                st.push(i);
            }
        }
    }
    cout << maxLength;

    return 0;
}

Code 2

#include<bits/stdc++.h>

#pragma GCC optimize(2)
using namespace std;

int main() {
    ios::sync_with_stdio(false);

    string s;
    cin >> s;

    stack<int> st;  // idx
    st.push(-1);
    int maxLength = 0;
    for (int i = 0; i < s.size(); ++i) {
        if (s[i] == ')' && !st.empty() && s[st.top()] == '(') {
            st.pop();
            maxLength = max(maxLength, i - st.top());
        } else {
            st.push(i);
        }
    }
    cout << maxLength;

    return 0;
}

3. 小明和二分图 - DFS

小明最近在学习二分图,由于小明现在还是初级阶段,什么都不会,这天小明的老师给小明这样一道题目,题目描述如下。给定一个图,判断这个图是不是二分图。小明看到这个问题犯了难,聪明的你可以帮助小明解决这个问题吗?

(这里给出一个二分图的通俗定义:顶点集 V 可分割为两个互不相交的子集 A,B,并且图中每条边依附的两个顶点都分属于这两个互不相交的子集,两个子集内的顶点不相邻,满足这样条件的图叫二分图。即:点集 A 或点集 B 内部不存在边,所有边都是 A 连到 B)

输入

本题包括多组测试数据(不超过20组) 第一行输入一个t,表示测试数据的数目; 对于每个测试数据,第一行输入两个数字n和m,表示图点的个数和边的个数; 接下来m行,每行两个数字u和v,表示点u和点v之间存在一条边。(1<= n<=500,1<=m<=70000)
Data Description: 对于10%的数据,1<= n<= 4; 对于50%的数据,1<= n<= 100; 对于100%的数据,1<= n<=500,1<=m<=70000。

输出

输出t行,对于每个测试数据,如果这个图是二分图输出“YES”,否则输出“NO”。

输入样例

2
4 4
1 3 
1 4
2 3
2 4
3 3
1 2
1 3
2 3

输出样例

YES
NO

代码

#include<bits/stdc++.h>

#pragma GCC optimize(2)
using namespace std;

enum {
    RED, BLUE, UNCOLORED
};

bool dfs(const vector<vector<int>> &graph, vector<int> &status, int color, int node) {
    status[node] = color;
    int nextColor = (color == RED) ? BLUE : RED;
    for (int nextNode:graph[node]) {
        if (status[nextNode] == color) return false;
        if (status[nextNode] == nextColor) continue;
        if (!dfs(graph, status, nextColor, nextNode)) return false;
    }
    return true;
}

bool solve(int n, int m) {
    vector<vector<int>> graph(n + 1);
    vector<int> status(n + 1, UNCOLORED);
    int x, y;
    while (m--) {
        cin >> x >> y;
        graph[x].push_back(y);
        graph[y].push_back(x);
    }
    for (int node = 1; node <= n; node++) {
        if (status[node] == UNCOLORED) {
            if (!dfs(graph, status, RED, node)) return false;
        }
    }
    return true;
}

int main() {
    ios::sync_with_stdio(false);

    int t, n, m;
    cin >> t;
    while (t--) {
        cin >> n >> m;
        cout << (solve(n, m) ? "YES" : "NO") << endl;
    }

    return 0;
}

4. 完全背包问题

N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是v[i],价值是c[i]

现在请你选取一些物品装入背包,使这些物品的体积总和不超过背包容量,且价值总和最大。

其中1<=N<=1001<=V<=500001<=v[i],c[i]<=10000

输入

第一行输入两个数N,V,分别表示物品种类数和背包容积; 之后N行,每行两个数v[i],c[i],分别表示第i种物品的体积和价值;

输出

输出一个数,表示最大的价值

输入样例

2 11
2 3
6 14

输出样例

20

代码 1 [超时]

#include<bits/stdc++.h>

#pragma GCC optimize(2)
using namespace std;

int weight[105], value[105], dp[50005];

int main() {
    ios::sync_with_stdio(false);

    int n, maxWeight;
    cin >> n >> maxWeight;
    for (int i = 1; i <= n; ++i) { cin >> weight[i] >> value[i]; }

    for (int i = 1; i <= n; ++i) {
        for (int s = maxWeight; s > 0; --s) {
            for (int k = 1; k <= s / weight[i]; ++k) {
                dp[s] = max(dp[s], dp[s - k * weight[i]] + value[i] * k);
            }
        }
    }

    cout << dp[maxWeight];

    return 0;
}

代码 2

#include<bits/stdc++.h>

#pragma GCC optimize(2)
using namespace std;

int weight[105], value[105], dp[50005];

int main() {
    ios::sync_with_stdio(false);

    int n, maxWeight;
    cin >> n >> maxWeight;
    for (int i = 1; i <= n; ++i) { cin >> weight[i] >> value[i]; }

    for (int i = 1; i <= n; ++i) {
        for (int s = weight[i]; s <= maxWeight; ++s) {
            dp[s] = max(dp[s], dp[s - weight[i]] + value[i]);
        }
    }

    cout << dp[maxWeight];

    return 0;
}

5. 数独 - 回溯

数独游戏规则如下:在9 * 9的盘面上有些已知的数字及未知的数字,推理出所有未知的数字,并满足每一行、每一列、每一个粗线宫内的数字均含1-9,不重复。

有些局面存在多个解无解,这属于不标准的数独。对于不标准的局面,输出No Solution。

输入

第1 - 9行,每行9个数中间用空格分隔,0表示该格子的数未知。

输出

如果局面是不标准的,输出No Solution,否则数据具体的解。

输入样例

0 6 0 5 9 3 0 0 0
9 0 1 0 0 0 5 0 0
0 3 0 4 0 0 0 9 0
1 0 8 0 2 0 0 0 4
4 0 0 3 0 9 0 0 1
2 0 0 0 1 0 6 0 9
0 8 0 0 0 6 0 2 0
0 0 4 0 0 0 8 0 7
0 0 0 7 8 5 0 1 0

输出样例

7 6 2 5 9 3 1 4 8
9 4 1 2 7 8 5 3 6
8 3 5 4 6 1 7 9 2
1 9 8 6 2 7 3 5 4
4 7 6 3 5 9 2 8 1
2 5 3 8 1 4 6 7 9
3 8 7 1 4 6 9 2 5
5 1 4 9 3 2 8 6 7
6 2 9 7 8 5 4 1 3

代码 1 [不考虑存在多解情况]

#include<bits/stdc++.h>

#pragma GCC optimize(2)
using namespace std;

int grid[9][9];

bool check(int x, int y, int val) {
    for (int i = 0; i < 9; ++i) {
        if (grid[x][i] == val || grid[i][y] == val) return false;
    }

    int r = x / 3 * 3, c = y / 3 * 3;
    for (int i = r; i < r + 3; ++i) {
        for (int j = c; j < c + 3; ++j) {
            if (grid[i][j] == val) return false;
        }
    }

    return true;
}

bool backtracking() {
    for (int i = 0; i < 9; ++i) {
        for (int j = 0; j < 9; ++j) {
            if (grid[i][j] != 0) continue;
            for (int val = 1; val <= 9; ++val) {
                if (check(i, j, val)) {
                    grid[i][j] = val;
                    if (backtracking()) return true;
                    grid[i][j] = 0;
                }
            }
            return false;
        }
    }
    return true;
}

int main() {
    ios::sync_with_stdio(false);

    for (int i = 0; i < 9; ++i) {
        for (int j = 0; j < 9; ++j) {
            cin >> grid[i][j];
        }
    }

    if (backtracking()) {
        for (int i = 0; i < 9; ++i) {
            for (int j = 0; j < 9; ++j) {
                cout << grid[i][j] << ' ';
            }
            cout << endl;
        }
    } else {
        cout << "No Solution" << endl;
    }

    return 0;
}

代码 2

#include<bits/stdc++.h>

#pragma GCC optimize(2)
using namespace std;

int grid[9][9], result[9][9];
int cnt = 0;

bool check(int x, int y, int val) {
    for (int i = 0; i < 9; ++i) {
        if (grid[x][i] == val || grid[i][y] == val) return false;
    }

    int r = x / 3 * 3, c = y / 3 * 3;
    for (int i = r; i < r + 3; ++i) {
        for (int j = c; j < c + 3; ++j) {
            if (grid[i][j] == val) return false;
        }
    }

    return true;
}

void backtracking() {
    if (cnt > 1) return;  // 发现多解, 退出函数
    for (int i = 0; i < 9; ++i) {
        for (int j = 0; j < 9; ++j) {
            if (grid[i][j] != 0) continue;
            for (int val = 1; val <= 9; ++val) {
                if (check(i, j, val)) {
                    grid[i][j] = val;
                    backtracking();
                    grid[i][j] = 0;
                }
            }
            return;  // 所有情况考虑完毕, 退出函数
        }
    }
    ++cnt;  // 找到可行解, 可行解数量 +1
    for (int i = 0; i < 9; ++i) {  // 保存答案
        for (int j = 0; j < 9; ++j) {
            result[i][j] = grid[i][j];
        }
    }
}

int main() {
    ios::sync_with_stdio(false);

    for (int i = 0; i < 9; ++i) {
        for (int j = 0; j < 9; ++j) {
            cin >> grid[i][j];
        }
    }

    backtracking();
    if (cnt == 1) {
        for (int i = 0; i < 9; ++i) {
            for (int j = 0; j < 9; ++j) {
                cout << result[i][j] << ' ';
            }
            cout << endl;
        }
    } else {
        cout << "No Solution" << endl;
    }

    return 0;
}

6. 小陶与杠铃片 - 线段树

小陶在举重队负责后勤工作。举重队的训练场中有一个区域一排码放了n片杠铃片,每天运动员们训练完之后会将杠铃片放回,之后小陶需要重新整理杠铃片的顺序,使它们由轻到重依次排好。由于杠铃片很重,小陶每次只能选两片相邻的杠铃片,交换它们的位置。现在小陶想知道,这一天他至少需要交换多少次才能整理完毕?

已知n<=2000000<=杠铃片重量<=200000

输入

第一行一个正整数n,表示有n片杠铃片;
第二行n个整数,表示运动员们放回后每片杠铃片依次的重量。

输出

输出一个整数,表示小陶至少交换的次数。

输入样例

10
16808 75250 50074 143659 108931 11273 27545 50879 177924 37710

输出样例

20

代码

#include<bits/stdc++.h>

#pragma GCC optimize(2)
using namespace std;

int segTree[800080];

void insert(int start, int end, int node, int val) {
    segTree[node]++;
    if (start == end) return;
    int mid = start + ((end - start) >> 1);
    int left_node = (node << 1) + 1, right_node = (node << 1) + 2;
    val <= mid ? insert(start, mid, left_node, val) : insert(mid + 1, end, right_node, val);
}

int query(int start, int end, int node, int L, int R) {
    if (R < start || L > end) return 0;
    if (L <= start && end <= R) return segTree[node];
    int mid = start + ((end - start) >> 1);
    int left_node = (node << 1) + 1, right_node = (node << 1) + 2;
    return query(start, mid, left_node, L, R) + query(mid + 1, end, right_node, L, R);
}

int main() {
    ios::sync_with_stdio(false);

    int n, val;
    long long ans = 0ll;
    cin >> n;
    while (n--) {
        cin >> val;
        ans += query(0, 200000, 0, val + 1, 200000);
        insert(0, 200000, 0, val);
    }

    cout << ans;

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值