第十六节 图论 - 1

AT_abc182_d 题解

洛谷链接&Atcoder 链接

本篇题解为此题较简单做法较少码量,并且码风优良,请放心阅读。

题目简述

从数轴的原点开始向正方向走。

第一次向前走 \(a_1\) 步,第二次向前走 \(a_1+a_2\),以此类推。

求走过的最大位置

思路

首先直接模拟时间复杂度 \(O(n^2)\),看一下数据范围 \((1 \le N \le 2 \times 10^5)\) 得知此方法会超时

那么就需要一点优化,用前缀和即可解决此题,没学过前缀和的建议看这个通过前缀和求出前 \(i\) 项的和就很容易解决此题了:

for(int i = 1; i <= n; i ++) {
		cin >> a[i];
		pre[i] = pre[i - 1] + a[i];
	}

预处理前缀和,在预处理的过程中同步求最大值,但因为此题的答案即最大值有可能在过程中产生故需要 \(O(N)\) 遍历前缀和求最大,此时间复杂度可以接受。

经过以上分析和前缀和优化,很容易即可得出代码了:

#include<iostream>
using namespace std;

int n, a[200005];
long long pre[200005], maxn[200005]; // 前缀和数组及最大前缀和数组

int main() {
	cin >> n;
	for(int i = 1; i <= n; i ++) {
		cin >> a[i];
		pre[i] = pre[i - 1] + a[i]; // 预处理前缀和
		maxn[i] = max(maxn[i - 1], pre[i]); // 预处理前缀和最大值
	}
	long long temp, ans = -0x3f3f3f3f; // ans 要求最大值,所以建议赋值一个足够小的负数
	for(int i = 1; i <= n; i ++) {
		ans = max(ans, maxn[i] + temp); // 遍历求最大值
		temp += pre[i]; // 一点点模拟
	}
	cout << ans << endl; // 输出,换行好习惯
	return 0;
}

提交记录

\[\text{The End!} \]

AT_arc113_c 题解

洛谷链接&Atcoder 链接

本篇题解为此题较简单做法较少码量,并且码风优良,请放心阅读。

题目简述

现在有一个字符串 \(S\),每一次你可以选择一个 \(i(1 \le i \le | S |)\),如果 \(S_i = S_{i + 1} \ne S_{i + 2}\)。就可以将 \(S_{i + 2}\) 设为 \(S_i\)

最多能操作几次。

思路

本题比较贪心,让我们先来造一个样例解释一下:

abbfioidddssabsaa 最优的方案是:

  • 先操作 \(4\) 次变为 abbfioidddsssssss

  • 再操作 \(7\) 次变为 abbfioidddddddddd

  • 最后操作 \(14\) 次变为 abbbbbbbbbbbbbbbb

这样最大总操作次数\(25\) 次。

从这个样例中可以发现贪心思路,要想总操作次数最大化,就需要从后到前去操作,如果从前到后操作那么后面可操作的连续字母就会被覆盖,这样总操作次数就不是最大了。

对于每次操作,最优的是把后面的所有不相同的字母变为一样,这就涉及到一个问题,如果后面有相同字母如何判断?其实不必再从当前位置往后搜,只需要定义一个 \(num\) 一维数组用 \(num_i\) 表示当前位置的后面字母 \(i\) 的个数

对于 \(num\) 数组需要在搜的过程中处理。如果遇到可以替换的情况就把当前位置后的字母全变为当前字母,同时需清空 \(num\) 数组的记录,把当前位置的字母数记录即可。

替换后,\(ans\) 需增加 \(n - i\),考虑到后面的相同字母,所以就需要用到我们维护\(num\) 数组了,所以操作数需减去 \(num_{str_{i}-'a'}\)

经过以上分析及优化后,很容易即可写出代码了:

#include<iostream>
using namespace std;

string str;
long long ans = 0, num[205];

int main() {
	cin >> str;
	int n = str.length(); // 记录 str 的长度
	num[str[n - 1] - 'a'] ++, num[str[n - 2] - 'a'] ++; // 初始化 num 数组
	for(int i = n - 3; i >= 0; i --) {
		num[str[i] - 'a'] ++; // 记录此位置的字母
   		// 满足替换的条件
		if(str[i + 1] != str[i + 2] && str[i] == str[i + 1]) {
			ans += n - i - num[str[i] - 'a'];
			for(int j = 0; j < 26; j ++) num[j] = 0; // 清空 num 数组
			num[str[i] - 'a'] = n - i; // 记录替换后的字母数
		}
	}
	cout << ans << endl; // 输出,换行好习惯
	return 0;
}

提交记录

\[\text{The End!} \]

A. 大回复术

题目描述

北方之大德鲁伊天空之怒释放的大回复术是一等一的神术。大回复术之所以能够有极强的治疗效果,是因为该法术能够以人体经络为基本,并建立至多两条(也可以一条都不建立)用于疏通能量的经络。

人体的经络可以看作一张 \(n\) 个点的图,里面有 \(m\) 条已经连接好的经络。大回复术能够新建至多两条经络。如果每一个点都有偶数条经络与之相连,则经络中的能量会立刻循环往复生生不息,达到治疗的效果。现在你需要回答的是,对于一个给出的经络图,是否能够通过施展大回复术使得其中的能量生生不息。

输入格式

第一行两个整数 \(n\)\(m\),接下来 \(m\) 行每行两个整数表示一条已经存在的经络。

输出格式

输出一行,为 true 或者 false

样例输入

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

样例输出

true

添加 4-5 即可。

样例输入

4 2
1 2
3 4

样例输出

true

添加 1-3、2-4 即可。

样例输入

4 3
1 2
1 3
1 4

样例输出

false

数据规模

\(0 \le n,m \le 10^5\)

点击查看代码
#include<iostream>
#include<vector>
using namespace std;

int n, m, num;
vector<int> vt[100005];

int main() {
    cin >> n >> m;
    if(n == 2 && m) {
        cout << "false\n";
        return 0;
    }
    for(int i = 1; i <= m; i ++) {
        int from, to;
        cin >> from >> to;
        vt[from].push_back(to);
        vt[to].push_back(from);
    }
    for(int i = 1; i <= n; i ++) {
        if(vt[i].size() % 2) num ++;
        if(vt[i].size() == n - 1 && vt[i].size() % 2) {
            cout << "false\n";
            return 0;
        }
    }
    if(num > 4 || num == n) cout << "false\n";
    else cout << "true\n";
    return 0;
}
编译结果
compiled successfully
time: 11ms, memory: 5848kb, score: 100, status: Accepted
> test 1: time: 2ms, memory: 5808kb, points: 10, status: Accepted
> test 2: time: 0ms, memory: 5748kb, points: 10, status: Accepted
> test 3: time: 1ms, memory: 5792kb, points: 10, status: Accepted
> test 4: time: 1ms, memory: 5696kb, points: 10, status: Accepted
> test 5: time: 2ms, memory: 5732kb, points: 10, status: Accepted
> test 6: time: 1ms, memory: 5724kb, points: 10, status: Accepted
> test 7: time: 1ms, memory: 5748kb, points: 10, status: Accepted
> test 8: time: 1ms, memory: 5724kb, points: 10, status: Accepted
> test 9: time: 1ms, memory: 5812kb, points: 10, status: Accepted
> test 10: time: 1ms, memory: 5848kb, points: 10, status: Accepted

B. 雪花图

题目描述

一个雪花图是由两个大于 \(1\) 的整数 \(x\)\(y\) 构成的:

从一个中心点开始

\(x\) 个点连接着中心点

\(x\) 个点中每个点都有y个点与其连接着

下面是当 \(x=5,y=3\) 时的一张雪花图:

上图的中心点为 \(15\)\(5\)\(x\) 点分别为:\((3,6,7,8,20)\),每个点都有 \(y=3\) 个点与其连着.

现在给你一张雪花图,请你确定该图 \(x\)\(y\) 的值.

输入格式

第一行包含一个正整数 \(t\) \((1 \le t \le 1000)\),表示测试数据组数

对于每个测试数据,第一行包含两个整数 \(n\)\(m\) \((2 \le n \le 200;1 \le m \le \min(1000,n(n−1)2))\),表示雪花图的点数和边数.

接下来 \(m\) 行每行包含两个整数 \(u\)\(v\) \((1 \le u,v \le n,u \ne v)\),代表一条边.

数据保证没有重边和自环并且一定是一张雪花图,\(x\)\(y\) 均大于 \(1\)

输出格式

对于每个测试数据,每行空格输出 \(x\)\(y\).

样例输入#1

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

样例输出#1

5 3
2 2
2 3
点击查看代码
#include<iostream>
#include<vector>
#include<string.h>
using namespace std;

int T, n, m;
vector<int> v[1005];

int main() {
    cin >> T;
    while(T --) {
        int num = 0;
        cin >> n >> m;
        for(int i = 1; i <= m; i ++) {
            int from, to;
            cin >> from >> to;
            v[from].push_back(to), v[to].push_back(from);
        }
        for(int i = 1; i <= n; i ++)
            if(v[i].size() == 1) num ++;
        int x = n - num - 1;
        int y = num / x;
        cout << x << " " << y << endl;
        for(int i = 1; i <= n; i ++) v[i].clear();
    }
    return 0;
}
编译结果
compiled successfully
time: 21ms, memory: 3512kb, score: 100, status: Accepted
> test 1: time: 0ms, memory: 3448kb, points: 10, status: Accepted
> test 2: time: 1ms, memory: 3484kb, points: 10, status: Accepted
> test 3: time: 0ms, memory: 3448kb, points: 10, status: Accepted
> test 4: time: 1ms, memory: 3496kb, points: 10, status: Accepted
> test 5: time: 4ms, memory: 3456kb, points: 10, status: Accepted
> test 6: time: 1ms, memory: 3492kb, points: 10, status: Accepted
> test 7: time: 2ms, memory: 3512kb, points: 10, status: Accepted
> test 8: time: 5ms, memory: 3460kb, points: 10, status: Accepted
> test 9: time: 5ms, memory: 3440kb, points: 10, status: Accepted
> test 10: time: 2ms, memory: 3496kb, points: 10, status: Accepted

C. 那是一棵树吗?

题目描述

树是一种常见的数据结构.

一棵树的根节点只有一个,并且没有其他点指向它.从根节点遍历有向边,我们只会得到唯一的一个序列.

例如,下图中前两个例子是树,第三个不是:



给你 \(t\) 张有向图,请你判断每张图是否是一颗树.

输入格式

第一行包含一个正整数 \(t\) \((1 \le t \le 2000)\),表示测试数据组数

对于每个测试数据,第一行包含两个整数 \(n\)\(m(2 \le n \le 200000;1 \le m \le 200000)\),表示图的点数和边数.

接下来 \(m\) 行每行包含两个整数 \(u\)\(v(1 \le u,v \le n,u \ne v)\),代表一条 \(u\) 指向 \(v\) 的边.

数据保证没有重边和自环.

对于所有测试数据,数据总量不超过 \(2 \times 10^5\).

输出格式

对于每个测试数据,输出 Case k is a tree.;如果第 \(k\) 个测试数据是一棵树,否则输出 Case k is not a tree.

样例输入#1

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

样例输出#1

Case 1 is a tree.
Case 2 is a tree.
Case 3 is not a tree.
点击查看代码
#include<iostream>
#include<vector>
#include<string.h>
#include<cmath>
#include<bits/stdc++.h>
using namespace std;

int T, n, m, vis[200005], num = 0;
vector<int> v[200005];

void dfs(int x) {
    vis[x] = true;
    for(int i = 0; i < v[x].size(); i ++) {
        if(vis[v[x][i]]) continue;
        dfs(v[x][i]);
    }
    return;
}

void f(int Case) {
    bool flag;
    cin >> n >> m;
    for(int i = 1; i <= n; i ++) v[i].clear(), vis[i] = false;
    for(int i = 1; i <= m; i ++) {
        int from, to;
        cin >> from >> to;
        v[from].push_back(to);
        v[to].push_back(from);
    }
    dfs(1);
    if(count(vis + 1, vis + n + 1, true) == n) flag = true;
    else flag = false;
    if(m != n - 1) flag = false;
    if(flag) printf("Case %d is a tree.\n", Case);
	else printf("Case %d is not a tree.\n", Case);
    return;
}

int main() {
    cin >> T;
    for(int j = 1; j <= T; j ++) f(j);
    return 0;
}
编译结果
compiled successfully
time: 456ms, memory: 15532kb, score: 100, status: Accepted
> test 1: time: 2ms, memory: 8224kb, points: 10, status: Accepted
> test 2: time: 46ms, memory: 8280kb, points: 10, status: Accepted
> test 3: time: 35ms, memory: 8168kb, points: 10, status: Accepted
> test 4: time: 16ms, memory: 8292kb, points: 10, status: Accepted
> test 5: time: 37ms, memory: 8272kb, points: 10, status: Accepted
> test 6: time: 45ms, memory: 8160kb, points: 10, status: Accepted
> test 7: time: 47ms, memory: 8284kb, points: 10, status: Accepted
> test 8: time: 47ms, memory: 8048kb, points: 10, status: Accepted
> test 9: time: 53ms, memory: 8180kb, points: 10, status: Accepted
> test 10: time: 128ms, memory: 15532kb, points: 10, status: Accepted
posted @ 2023-07-27 19:38  So_noSlack  阅读(114)  评论(0编辑  收藏  举报