七题题解

A

科普

  1. getline

在 OI 中,经常用 getline(cin, s) 读入一行(包含空格)到 s 中。

  1. string::find

假如有两个叫 a,bstring

那么 a.find(b) 返回 ba 中第一次出现的位置的下标。

如果 b 没有在 a 中出现过,返回 -1

  1. \ 的转义字符

像换行是 \n 一样,在字符串中,\\ 表示一个 \

代码

#include <iostream>
#include <string>
using namespace std;
string s;
int main()
{
    getline(cin, s);
    if(s.find("\\r\\n") != -1) cout << "windows";
    else if(s.find("\\n") != -1) cout << "linux";
    else cout << "mac";
    return 0;   
}

B

思路

把入栈的每一堆团子当成一个整体。

出栈时一堆一堆地往外出,在需要把一堆拆开时就拆开。

善用等差数列求和公式。

代码

有一些细节。

注意栈的判空。

#include <iostream>
#include <stack>
#include <utility>
#include <cstdio>
#define int long long
using namespace std;
int n;stack<pair<int, int> > s;
signed main()
{
    cin >> n;
    for(int i = 0, l, r, k, o;i < n;++i)
    {
        cin >> o;
        if(o == 1) cin >> l >> r, s.push(make_pair(l, r)); //成堆入栈
        else
        {
            cin >> k;int cnt = 0, ans = 0; //cnt记录拿了多少团子,ans记录拿出团子的总价值
            while(!s.empty()) //注意
            {
                int l = s.top().first, r = s.top().second; //拿出栈顶的一堆团子
                if(cnt + r - l + 1 > k) break; //再拿就超过k个了
                cnt += r - l + 1;ans += (l + r) * (r - l + 1) / 2;s.pop(); //记录cnt和ans
            }
            if(s.empty()) //刚好把栈拿完,肯定不需要再拿了
            {
                cout << ans << endl;
                continue;
            }
            //读者自行思考为什么没有上面的if会RE?
            int l = s.top().first, r = s.top().second;s.pop(); //拿出栈顶的一堆团子
            int lst = k - cnt; //还要拿多少个
            ans += (r - lst + 1 + r) * lst / 2; //从这一堆团子中拿出还要拿的
            r -= lst;s.push(make_pair(l, r)); //剩下的放回栈顶
            cout << ans << endl;
        }
    }
    return 0;
}

C

科普

to_string(int x):将整数 x 转化为字符串

思路

数据范围明显搜索题。

只需要搜索每一条路线,搜索时顺便记录路线即可。

代码

#include <iostream>
#include <string>
using namespace std;
struct Edge{int v, nxt;}edge[1050];
int n, a[50], head[50], cnt, maxn;string ans;
void add(int u, int v)
{
    ++cnt;
    edge[cnt].v = v;
    edge[cnt].nxt = head[u];
    head[u] = cnt;
}
void dfs(int x, int dep, string s) //x:当前节点,dep:当前地雷数,s:当前路线
{
    s += "-" + to_string(x); //记录路线
    for(int i = head[x];i;i = edge[i].nxt)
        dfs(edge[i].v, dep + a[edge[i].v], s);
    if(dep > maxn) maxn = dep, ans = s.substr(1, s.length() - 1);
}
int main()
{
    cin >> n;
    for(int i = 1;i <= n;++i)
        cin >> a[i];
    for(int l, r;;)
    {
        cin >> l >> r;
        if(l == 0 && r == 0) break;
        add(l, r);
    }
    for(int i = 1;i <= n;++i)
        dfs(i, a[i], "");
    cout << ans << endl << maxn;
    return 0;
}

D

思路

我们把切木板的过程看成合并木板,每次合并的花费 = 两块木板的长度和。

合并果子相信都做过,贪心思路就不再说了。

代码

合并果子原代码能过。

#include <iostream>
#include <queue>
#define int long long
using namespace std;
priority_queue<int, vector<int>, greater<int> > q;int n, ans;
signed main()
{
    cin >> n;
    for(int i = 0, t;i < n;i++)
        cin >> t, q.push(t);
    while(q.size() != 1)
    {
        int a = q.top();q.pop();
        int b = q.top();q.pop();
        q.push(a + b);ans += a + b;
    }
    cout << ans;
    return 0;
}

E

科普

stoi(string s):将 s 转化为整数。

思路

没啥好说的,纯模拟。

注意题目里的一句话

如果只是原地转弯,从开始到最后从来不走动,则输出“(0,0)”。

代码

#include <iostream>
#include <string>
using namespace std;
int n, f = 3, x, y, vx[4] = {-1, 0, 1, 0}, vy[4] = {0, -1, 0, 1};bool flg = 1;
int main()
{
    cin >> n;
    for(int i = 0;i < n;++i)
    {
        string s;cin >> s;
        if(s == "right") f = (f == 0 ? 3 : f - 1);
        else if(s == "left") f = (f + 1) % 4;
        else
        {
            int t = stoi(s);
            x += vx[f] * t, y += vy[f] * t;
            cout << "(" << x << "," << y << ")" << endl;flg = 0;
        }
    }
    if(flg) cout << "(0,0)";
    return 0;
}

F

有一定思维难度。

光看思路长度也能看出这题放在水题比赛严重不合适啊

思路

第一部分

不难想到,需要先找到每个房子所对应的最佳现有灯

根据题意,使灯功率 - 房子功率最小的灯是这个房子的最佳现有灯。

我们可以把房子所需亮度和灯的亮度放在一个数组里,然后把数组升序排序。

排序后,灯和房子越接近,对于那个房子来说灯就更好。(当然前提是灯在房子后面)

开一个栈,用来存放当前没有找到灯的房子

然后把数组扫一遍:

  • 对于房子,直接入栈

  • 对于灯:
    1. 栈是空的:没有比这个灯更小的房子和这个灯匹配,这个灯没有用。
    2. 栈不是空的:栈顶是前面的没有匹配的房子中最大的,也就和这个灯最接近。所以栈顶和这个灯匹配,记录 灯功率 - 栈顶。

当然,有可能灯和房子功率一样,而灯排序后在房子前面,灯就浪费掉了。

所以在灯和房子功率一样时,把灯放在房子后面。

第二部分

上面说了,最佳现有灯要按灯功率 - 房子功率来比较,而非灯的功率。

所以上面记录的是灯功率 - 房子功率,而不是灯。

那么记录了灯功率 - 房子功率,怎么求灯总功率呢?

容易想到,先累加房子功率,再累加记录的灯功率 - 房子功率,就是灯总功率了。

(直接把灯的数组累加一遍不就是灯总功率了吗)

第三部分

然而我们还要更改 k 个灯。

那既然有一些灯没有用,就会有一些房子没有匹配灯。

那么这些房子就必须要单独买灯(现有的没法满足要求)。

有多少个房子呢?栈里剩下的房子就是没有匹配的。

  • 所以栈的大小 > k,显然买不完,NIE

  • 如果栈的大小 ≤ k,可以买完,k -= 栈的大小。

显而易见,要买肯定买刚好的,所以这些房子的 灯功率 - 房子功率 = 0,累加时不用考虑它们。


如果还可以改呢?那就要改 灯功率 - 房子功率 较大的了。

刚才一直没有说把 灯功率 - 房子功率 记录在哪里,

这里为了方便,把它们扔进一个大根堆。

然后从堆顶往下改,直到 k 次用完为止。

显而易见,要买肯定买刚好的,所以这些房子的 灯功率 - 房子功率 = 0。

所以修改之后直接弹出即可,累加时不用考虑它们。


那还要考虑谁?仍然在大根堆里的。

把堆一个一个弹空,累加 灯功率 - 房子功率。

加上之前累加的房子功率的和,就是答案了。

代码

#include <iostream>
#include <utility>
#include <stack>
#include <queue>
#include <algorithm>
#define int long long
#define v first
#define d second
using namespace std;
int n, nd, k, ans;pair<int, bool> a[1000050];stack<int> s;priority_queue<int> q;
signed main()
{
    cin >> n >> k;
    for(int i = 0;i < 2 * n;++i)
    {
        cin >> a[i].v;
        if(i < n) a[i].d = 1;
        if(i >= n) ans += a[i].v;
    }
    sort(a, a + 2 * n);
    for(int i = 0;i < 2 * n;++i)
    {
        if(!a[i].d) s.push(a[i].v);
        else if(!s.empty()) q.push(a[i].v - s.top()), s.pop();
    }
    if(s.size() > k) {cout << "NIE";return 0;}
    k -= s.size();
    while(k--) q.pop();
    while(!q.empty())
        ans += q.top(), q.pop();
    cout << ans;
    return 0;
}

G

思路

没什么好说的。直接枚举每个花的颜色,判断是否符合要求。

复杂度 $O(4^nm)$,能过。

代码

#include <iostream>
#include <utility>
#define int long long
using namespace std;
int a[50], n, m, ans;pair<int, int> e[150];
void dfs(int x)
{
    if(x > n)
    {
        for(int i = 0;i < m;++i)
            if(a[e[i].first] == a[e[i].second]) return;
        ++ans;return;
    }
    for(int i = 1;i <= 4;++i)
        a[x] = i, dfs(x + 1);
}
signed main()
{
    cin >> n >> m;
    for(int i = 0;i < m;++i)
        cin >> e[i].first >> e[i].second;
    dfs(1);
    cout << ans;
    return 0;
}
posted @ 2021-10-17 16:02  Jijidawang  阅读(3)  评论(0编辑  收藏  举报  来源