扫地机器人(第十届蓝桥杯研究生组)
问题描述
如图,在一块长为N的方格地板上,放置K个扫地机器人(图中黑点),每个机器人都可以左右移动,且互不干扰,扫完地后又都回到原位置,为公平起见,每个机器人的工作量尽可能相同,求移动步数最多的机器人移动的步数。(题目大概是这个意思,欢迎来更正)
输入格式:
第一行输入两个整数:N、K
第二行输入K个整数,表示扫地机器人的位置
输出格式:
输出一个整数,表示移动步数最多的机器人移动的步数
案例:
输入:
10 3
3 5 8
输出:
6
说明:
总移动步数最少的方案为:
1号机器人路线:3->2->1->2->3->4->3
2号机器人路线:5->6->7->6->5
3号机器人路线:8->9->10->9->8
方法一
算法思想
假设每个机器人的位置为a[i],对应扫地区间为[xi,yi](i=1,2,…,K),显然x1<=a[i]<=yi。于是,问题转化为:寻找一组区间(K个),在区间长度尽可能一致的情况下,使这组区间能够覆盖满1~N,并且每个区间内至少有一个机器人(否则至少有一个区间内没有机器人,即该区间没有机器人扫,无意义)。设最长的区间长度为L,则移动步数最多的机器人移动了2*(L-1)步。
在不影响最长区间长度的情况下,为方便计算,不妨设每个区间的长度都为L。显然,N/K<=L<=N。为方便读者理解笔者思路,下面给出几个特例图。
算法步骤
- 1 将机器人的位置a[ ]在方格b[ ]中标记,并对a[ ]排序
- 2 将方格划分成若干个区间D1,D2,…,Dm,满足:
- 3 对L从N/K到N,first_L从L到1开始遍历,直到找到一个L和first_L满足在每个区间内至少有一个机器人,输出L并结束程序。
说明:
- 之所以要改变第1个区间的长度,因为会出现如下情况:
当区间长度L=6时,系统会误判区间长度不够。实际上,L=6够了,如下: - 之所以只改变第1个区间的长度,因为区间之间是紧挨着的,缩小第1个区间的长度,后面的区间都会向左平移,其实第1个区间也向左平移了,只是前一部分越界了没显示。
算法实现
#include<iostream>
#include<algorithm>
using namespace std;
int N,K;
int a[100]; //K个机器人位置
int b[100]; //标记N个方格中是否有机器人
int check1(int first_L,int L){ //第一个区间长度为first_L,之后区间长度都为L
int i,j;
if(first_L+(K-1)*L<N){
return 0;
}
i=1; //第i个区间
j=1; //当前查看的方格位置
while(j<=N){
if(b[j]==1){ //第i个区间内有机器人
j=first_L+(i-1)*L+1; //j指向下一个区间起点
i++; //下一个区间
}else{
j++;
if(j==first_L+(i-1)*L+1||j==N+1){ //第i个区间内没有机器人
return 0;
}
}
}
return 1;
}
int check(int L){
int first_L; //首区间的长度(取值范围:1~L)
for(first_L=L;first_L>0;first_L--){
if(check1(first_L,L)){
return 1;
}
}
return 0;
}
int fun(){
int i,j,L;
for(L=N/K;L<=N;L++){
if(check(L)){
return L;
}
}
}
int main(){
int i,L;
cin>>N>>K;
for(i=1;i<=K;i++){
cin>>a[i];
b[a[i]]=1; //标记a[i]位置有机器人
}
sort(a+1,a+K+1);
L=fun();
cout<<2*(L-1);
return 0;
}
算法思想的正确性还有待考证,欢迎热心的读者批评指正!
方法二
算法思想
如果每个机器人的活动区间确定了,其最大的移动步数可根据2*(L-1)求得。为减轻每个机器人的任务,限制相邻的机器人的活动范围不相交。
第1个机器人的活动范围:1~a[2]-1,其右边界活动范围:a[1] ~ a[2]-1;
第2个机器人的活动范围:a[1]+1~a[3]-1,其右边界活动范围:a[2] ~ a[3]-1;
第3个机器人的活动范围:a[2]+1~a[4]-1,其右边界活动范围:a[3] ~ a[4]-1;
…
第K-1个机器人的活动范围:a[K-2]+1~a[K]-1,其右边界活动范围:a[K-1] ~ a[K]-1;
第K个机器人的活动范围:a[K-1]+1~N,其右边界活动范围:N ~ N;
第1个机器人的左边界已确定,如果其右边界确定了,第二个机器人的左边界就确定了,即后面的机器人的左边界依赖于前面机器人的右边界,由此可用dfs对每个机器人的右边界的所有情况进行遍历,每找到一种区间划分法,检验是否优于当前解
算法实现
#include<iostream>
#include<algorithm>
#define INF 10000000
using namespace std;
struct neighbor{ //机器人活动范围
int l; //左边界
int r; //右边界
};
int N,K;
int a[100]; //K个机器人位置
neighbor B[100]; //K个机器人的最大移动邻域
neighbor F[100]; //K个机器人的移动邻域
int min_max_L=INF; //最小的最大邻域长度
void init(){ //初始化函数
int i;
cin>>N>>K;
for(i=1;i<=K;i++){
cin>>a[i];
}
sort(a+1,a+K+1);
B[1].l=1;
B[K].r=N;
if(K==1){
B[1].r=N;
}else{
B[1].r=a[2]-1;
B[K].l=a[K-1]+1;
}
for(i=2;i<K;i++){
B[i].l=a[i-1]+1;
B[i].r=a[i+1]-1;
}
F[1].l=1; //第1格一定由第1个机器人扫
F[K].r=N; //第N格一定由第K个机器人扫
}
int max_L(){ //最大区间长度
int i,L,m=-INF;
for(i=1;i<=K;i++){
L=F[i].r-F[i].l+1;
if(L>m){
m=L;
}
}
return m;
}
void search(int index,int left){ //第index个机器人,其左边界为left
int i,L;
F[index].l=left; //第index个机器人的左边界
if(index==K){
L=max_L();
if(L<min_max_L){
min_max_L=L;
}
return;
}
for(i=a[index];i<=B[index].r;i++){
F[index].r=i; //第index个机器人的右边界
search(index+1,i+1);
}
}
int main(){
int i,L;
init(); //初始化
search(1,1);
cout<<2*(min_max_L-1);
return 0;
}