ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

20200715模拟赛3题解

2020-07-17 19:03:59  阅读:442  来源: 互联网

标签:20200715 head 小轩 int 题解 中中 杀手 include 模拟


A. 中中救援队

题目描述

  • 中中酷爱滑雪,某日突发奇想,带领所有BDEZ的OIER去Alps滑雪,不幸的是,中中和OIER们遭遇了雪崩,除了中中,所有的OIER们都埋在了雪坑里,此时,中中救援队闪亮登场~!(中中救援队只有中中一个人!Orz!)
  • 雪崩之后,出现了N个雪坑,每个雪坑都有一名OIER深陷其中,只有中中幸免,现在中中找到了M条双向道路,每条道路都会连接两个雪坑,但是,由于中中是路痴(-_-||),所以中中希望去除M条道路中尽可能多的道路,但是还要保证任一雪坑都能到达其他的雪坑,所以要先选择留下N-1条道路,中中可以从任意一个雪坑出发,并且任务完成后要回到出发点(起点的雪坑和终点的雪坑也需要消耗体力),而且中中只记得他选择的道路 中中每到一个雪坑i,都会消耗一定的体力值t[i],即使这个雪坑的OIER已被救出。第j条道路连接x,y两个雪坑,而从x到达y也需要消耗体力值energy 由于时间紧迫,中中请你这名OIER帮助他计算下救出这N名OIER所消耗的最小体力值是多少

输入格式

  • 输入第一行两个整数N和M,表示有N名OIER,M条连接的道路
  • 接下来N行,每行一个整数t[i],表示第i个雪坑需要消耗中中的体力值
  • 然后M行,每行三个整数x,y,energy,表示从x坑滑到y坑需要消耗的体力值为energy

输出格式

  • 第1行,一个整数,为中中消耗的最小体力值

样例输入

5 7
6
5
13
8
18
4 1 7
5 2 5
1 5 16
2 3 20
3 1 18
4 3 12
2 4 15

样例输出

154

数据范围与提示

  • 对于30%的数据 5<=N<=100,N-1<=M<=500 1<=t[i]<=100 x<=N,y<=N,x<>y,1<=energy<=100
  • 对于全部数据 5<=N<=10000,N-1<=M<=100000 1<=t[i]<=1000 x<=N,y<=N,x<>y,1<=energy<=1000
  • 结果保证不超过 \(2^{31}-1\)

Solve

  • 安慰奶牛改变了一下题目
  • 题目要求先选出n-1条边,一看就像最小生成树,需要求的是中中从一个点走完所有点并回来的最小费用,每条边都要走两次,走这条边两端的点都要走,所以可以将一条边的边权赋值为边权的两倍加上两端点的点权,然后跑最小生成树就可以了。
  • 需要注意的是起点会多走一次,就选取一个点权最小的当起点,最后加上这个点的点权就好了。

Code

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e4+5, M = 1e5+5;
struct Node {
    int x, y, d;
    bool operator < (const Node &b) const {
        return d < b.d;
    }
}a[M];
int n, m, w[N], f[N], s = 1<<30;
int found(int x) {//并查集
    return f[x] == x ? x : (f[x] = found(f[x]));
}
int Kru() {
    int ans = 0, cnt = 0;
    sort(a + 1, a + m + 1);
    for (int i = 1; i <= n; ++i)
        f[i] = i;
    for (int i = 1; i <= m; ++i) {
        int x = found(a[i].x), y = found(a[i].y);
        if (x == y) continue;
        f[x] = y;
        ans += a[i].d;
        if (++cnt == n - 1) return ans;
    }
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &w[i]), s = min(s, w[i]);
    for (int i = 1; i <= m; ++i)
        scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].d),
        a[i].d = a[i].d * 2 + w[a[i].x] + w[a[i].y];//这里最重要
    printf("%d\n", Kru() + s);
    return 0;
}

B. 家务活

题目描述

  • 农场主约翰家人有N (3 <= N<= 10,000)件家务活需要完成,完成第i件家务活需要Ti(1 <=Ti<= 100)的时间,在做第i件家务活之前约翰必须完成若干个家务活,我们称这些家务为i的必备家务。至少有一个家务没有必备家务,第一件家务没有必备家务。
  • 约翰已经安排好完成家务活的顺序,家务活k的必备家务活只会出现在区间[1,k-1]之间。没有依赖关系的家务活可以同时进行。
  • 现在请你计算约翰家人完成所有家务的最短时间。

输入格式

  • 第一行为一个整数N,表示有N件家务活。
  • 接下来2~n+1行,第i+1行前两个数分别为Ti和ki,表示完成第i件家务需要Ti的时间,有ki个必备家务,接着k个数表示第i件家务的必备家务。

输出格式

  • 只有一行,约翰完成所有家务的最短时间。

样例输入

7
5 0
1 1 1
3 1 2
6 1 1
1 2 2 4
8 2 2 4
4 3 3 5 6
```cpp
样例输出
```cpp
23

数据范围与提示

  • 1: 0 时刻开始,5 时刻结束
  • 2: 5 时刻开始,6 时刻结束
  • 3: 6 时刻开始,9 时刻结束
  • 4: 5 时刻开始,11时刻结束
  • 5: 11时刻开始,12时刻结束
  • 6: 11时刻开始,19时刻结束
  • 7: 19时刻开始,23时刻结束

Solve

  • 一看到题目中“家务活k的必备家务活只会出现在区间[1,k-1]之间”就能想到是线性DP,还有大佬写拓扑,这DP他不香吗?
  • 每个家务由他的必备家务中最晚完成的转移过来。

Code

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e4+5;
int n, f[N], ans;
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        int t, k, x;
        scanf("%d%d", &t, &k);
        while (k--) {
            scanf("%d", &x);
            f[i] = max(f[i], f[x]);
        }
        f[i] += t;
        ans = max(ans, f[i]);
    }
    printf("%d\n", ans);
    return 0;
}

C. 传纸条

题目描述

  • 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学被安排坐成一个m行、n列的矩阵,而小渊和小 轩被安排坐在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n)。从小渊传给小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。
  • 在活动进行中,小渊希望给小轩传一张纸条,同时希望小轩给他回复。班里的每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。
  • 还有一件事情需要注意,全班每个同学愿意帮忙的好心程度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用0表示),可以用一个 O-- 100的自然数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度之和最大。现在,请你帮助小渊和小轩找到这样两条路径。

输入格式

  • 第一行有2个用空格隔开的整数m和n,表示班里有m行n列 (1<=m,n<=50)。
  • 接下来的m行是一个m*n的矩阵,矩阵中第i行j列的整数表示坐在第i行j列的学生的好心程度。每行的n个整数之间用空格隔开。

输出格式

  • 共一行,包含一个整数,表示来回两条路上参与传纸条的同学的好心程度之和的最大值。

样例输入

3 3
0 3 9
2 8 5
5 7 0

样例输出

34

数据范围与提示

  • 30%的数据满足:l<=m,n<=10
  • 100%的数据满足:1<=m,n<=50

Solve

  • 差一点就AK了!!!,没有判断越界的情况QwQ
  • 这道题用3维,4维我不太会,如果数据范围大的话,3维还可以把第一维滚动起来。
  • f[k][i][j]表示横纵坐标和为k时,第一个人位置横坐标为i,第二个人位置横坐标为j时得到的最大的和

Code

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 60;
int n, m, f[N<<1][N][N], a[N][N];
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            scanf("%d", &a[i][j]);
    for (int k = 3; k < n + m; ++k)
        for (int i = 1; i <= min(n, k); ++i)//这里和下面没取min就是我WA90的原因
            for (int j = i + 1; j <= min(n, k); ++j)
                f[k][i][j] = max(max(f[k-1][i][j], f[k-1][i-1][j-1]), max(f[k-1][i-1][j], f[k-1][i][j-1])) + a[i][k-i] + a[j][k-j];
    printf("%d\n", f[n+m-1][n-1][n]);
    return 0;
}

D. 杀人游戏

题目描述

  • 一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面查出谁是杀手。
  • 警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是杀手, 谁是平民。假如查证的对象是杀手,杀手将会把警察干掉。
  • 现在警察掌握了每一个人认识谁。
  • 每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。
  • 问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?

输入格式

  • 第一行有两个整数 N,M。
  • 接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x,例如President同志) 。

输出格式

  • 仅包含一行一个实数,保留小数点后面 6 位,表示最大概率。

样例输入

5 4 
1 2 
1 3 
1 4 
1 5 

样例输出

0.800000

数据范围与提示

  • 警察只需要查证 1。假如1是杀手,警察就会被杀。假如 1不是杀手,他会告诉警察 2,3,4,5 谁是杀手。而 1 是杀手的概率是 0.2,所以能知道谁是杀手但没被杀的概率是0.8。
  • 对于 100%的数据有 1≤N ≤ 10 0000,0≤M ≤ 30 0000

Solve

  • 这是这几个里最难的,想了半天,居然过了,也是很奇怪
  • 具体内容看我的博客

Code1

//第一种方法
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+5;
struct Side {
    int t, next;
}e[N*3];
int head[N], tot;
void Add(int x, int y) {//加边
    e[++tot] = (Side) {y, head[x]};
    head[x] = tot;
}
int n, m, d[N], s, ans;
bool v[N], f;
void Dfs(int x) {//染色
    s--;
    v[x] = 1;
    for (int i = head[x]; i; i = e[i].next)
        if (!v[e[i].t]) Dfs(e[i].t);
}
int main() {
    scanf("%d%d", &n, &m);
    s = n;
    while (m--) {//建立单向边并统计入度
        int x, y;
        scanf("%d%d", &x, &y);
        Add(x, y);
        ++d[y];
    }
    for (int i = 1; i <= n && s; ++i) {//正向遍历入度为0的点
        if (d[i]) continue;
        int l = s;
        Dfs(i);
        ++ans;
        if (l - s == 1) f = 1;//l-s是本次染色染了几个点
    }
    memset(v, 0, sizeof(v));
    ans = 0; 
    s = n;//进行初始化
    for (int i = n; i && s; --i) {//反向遍历入度为0的点
        if (d[i]) continue;
        int l = s;
        Dfs(i);
        ++ans;
        if (l - s == 1) f = 1;//同上
    }
    for (int i = 1; i <= n && s; ++i) {//遍历在环中的点
        if (v[i]) continue;
        int l = s;
        Dfs(i);
        ++ans;
        if (l - s == 1) f = 1;//同上
    }
    if (f) --ans;//如果有孤立点就有一个人可以不用询问
    printf("%.6lf", (double)(n - ans) / n);
    return 0;
}

Code2

//第二种方法
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5+5;
struct Side {
    int t, next;
}e[N*3];
int head[N], tot;
void Add(int x, int y) {
    e[++tot] = (Side) {y, head[x]};
    head[x] = tot;
}
int n, m, d[N], s, ans;
bool v[N], f;
bool judge(int x) {//如果返回0,则这个点可以当作孤立点,要在第二次for循环中遍历
    for (int i = head[x]; i; i = e[i].next)
        if (d[e[i].t] != 1) return 0;
    return 1;
}
void Dfs(int x) {//染色
    s--;
    v[x] = 1;
    for (int i = head[x]; i; i = e[i].next)
        if (!v[e[i].t]) Dfs(e[i].t);
}
int main() {
    scanf("%d%d", &n, &m);
    s = n;
    while (m--) {//建单向边,统计入度
        int x, y;
        scanf("%d%d", &x, &y);
        Add(x, y);
        ++d[y];
    }
    //第一次遍历入度为0且他的子节点只有他这唯一的父节点
    for (int i = 1; i <= n && s; ++i) {
        if (d[i] || !judge(i)) continue;
        int l = s;
        Dfs(i);
        ++ans;
        if (l - s == 1) f = 1;
    }
    //第二次遍历入度为0且他的子节点不只有他这唯一的父节点
    for (int i = 1; i <= n && s; ++i) {
        if (d[i] || v[i]) continue;//这里添了一个v是不要去遍历第一个循环遍历过的点
        int l = s;
        Dfs(i);
        ++ans;
        if (l - s == 1) f = 1;
    }
    //第三次遍历在环中的点
    for (int i = 1; i <= n && s; ++i) {
        if (v[i]) continue;
        int l = s;
        Dfs(i);
        ++ans;
        if (l - s == 1) f = 1;
    }
    if (f) --ans;
    printf("%.6lf", (double)(n - ans) / n);
    return 0;
}

标签:20200715,head,小轩,int,题解,中中,杀手,include,模拟
来源: https://www.cnblogs.com/Z8875/p/13331523.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有