目录:算法日记
题目来源:1738. 蹄球 - AcWing题库
题目描述
为了准备即将到来的蹄球锦标赛,Farmer John 正在训练他的 头奶牛(方便起见,编号为 )进行传球。
这些奶牛在牛棚一侧沿直线排列,第 号奶牛位于距离牛棚 的地方。
每头奶牛都在不同的位置上。
在训练开始的时候,Farmer John 会将若干个球传给不同的奶牛。
当第 号奶牛接到球时,无论是从 Farmer John 或是从另一头奶牛传来的,她会将球传给最近的奶牛(如果有多头奶牛与她距离相同,她会将球传给这些奶牛中最左边的那头奶牛。)。
为了使所有奶牛都有机会练习到传球,Farmer John 想要确保每头奶牛都持球至少一次。
帮助他求出为了达到这一目的他开始时至少要传出的球的数量。
假设他在开始的时候能将球传给最适当的一组奶牛。
输入格式
输入的第一行包含 。
第二行包含 个用空格分隔的整数,其中第 个整数为 。
输出格式
输出 Farmer John 开始的时候最少需要传出的球的数量,使得所有奶牛至少持球一次。
数据范围
输入样例
5
7 1 3 11 4
输出样例
2
算法思路
核心规则为,奶牛会将球传给最近的奶牛,如果距离相等则左传。在题目中,球是一传一的,因此对于每一个传出的球,一定存在一个回传的位置T,将球回传给上一个位置P,使得在T与P之前形成一个环。对应产生两种情况,如下图所示。
由上图可以看出,对于图中的每个顶点,传给谁是唯一确定的,而谁传球给我多出一种回传的情况(入度为)和Farmer传球给我情况(入度为),因此对于每个顶点,入度,出度。则对于图中每块(可能存在多块)的一般情况,如下图所示。
要确保每头奶牛都持球至少一次,就需要按有向边的方向遍历到所有顶点。对于上图的一般情况,则农夫仅需要向入度为0的顶点传球,就能保证该块内的所有顶点都可以被遍历到。
在此题中可能存在一种特殊块不能被一般情况覆盖,如下图所示。该块仅存在环,不存在入度为的顶点,但仍需传入一个球。对于这种情况,需要判断是否是环(判断顶点A的后继的后继是否是A)以及环上两个顶点的入度是否为。为了方便计算,如果存在该情况,每个顶点则需传入个球(为避免小数存在精度问题,在统计时可当作整数处理)。
AC代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 110;
const int INF = 1e9;
int nums[N];
int in_node[N]; //入度
int next_node[N]; //后继
int n;
int main() {
cin>>n;
for(int i = 1; i <= n; ++i) cin>>nums[i];
nums[0] = -INF;
nums[n + 1] = INF;
sort(nums + 1, nums + 1 + n);
for(int i = 1; i <= n; ++i) {
if(nums[i] - nums[i - 1] <= nums[i + 1] - nums[i]) {
next_node[i] = i - 1;
in_node[i - 1] += 1;
} else {
next_node[i] = i + 1;
in_node[i + 1] += 1;
}
}
int res = 0;
for(int i = 1; i <= n; ++i) {
if(in_node[i] == 0) res += 2;
//是否有环,环上两点入度是否为1
else if(next_node[next_node[i]] == i && in_node[i] == 1 && in_node[next_node[i]] == 1) res += 1;
}
cout<<res / 2<<endl;
return 0;
}