——AD HOC
——贪心
原题传送门
前言
这是一道有纪念意义的题目.
在心里默默地说:
一定要好好write哦!
“I will.”
Description
n people wish to cross a bridge at night.
A group of at most two people may cross at any time, and each group must have a flashlight.
Only one flashlight is available among the n people, so some sort of shuttle arrangement must be arranged in order to return the flashlight so that more people may cross.
Each person has a different crossing speed; the speed of a group is determined by the speed of the slower member. Your job is to determine a strategy that gets all n people across the bridge in the minimum time.
题目大意
- 有n个人想要过桥.
- 任何时刻桥上最多有2个人.
- 过桥需要手电筒,而n个人只有1个手电筒,所以到对岸的人需要回来传递手电筒.
- 两个人过桥的时间是过桥较慢的那个人过桥所需时间.
- 已知所有人的过桥时间T1—Tn ,求所有人过桥所需的最小时间.
Data
Input
The first line of input contains n, followed by n lines giving the crossing times for each of the people.
Output
The first line of output must contain the total number of seconds required for all n people to cross the bridge.
The following lines give a strategy for achieving this time.
Each line contains either one or two integers, indicating which person or people form the next group to cross.
(Each person is indicated by the crossing time specified in the input. Although many people may have the same crossing time the ambiguity is of no consequence.)
Note that the crossings alternate directions, as it is necessary to return the flashlight so that more may cross.
If more than one strategy yields the minimal time, any one will do.
1<=n<=1000 Ti<=100
Sample Input
4
1
2
5
10
Sample Output
17
1 2
1
5 10
2
1 2
数据
输入:
第一行一个正整数 n.
第二到第 n+1 行,n 个正整数 Ti 表示第 i 个人的过桥时间.
输出:
第一行一个正整数,表示所有人过桥的最小时间.
接下来若干行,表示每一次过桥的人过桥 所用时间 ,中间1个空格隔开.
思路
综合了AD HOC和贪心的一道好题.
因为要输出人员过桥的时间,
所以不需要考虑人员的序号,只需注意他们的大小关系.
So,我们可以对 T 排序.
接下来,我们正式考虑此题.
为了方便叙述,我们分几个模块进行分析.
形成算法
设排完序的时间序列为A
那么下面考虑An如何过桥.
因为An过桥最慢,所以无论如何过桥,时间都是An.
既然这样了,我们应该选择最快的人传递手电筒.
而我们应该尽量让两个人过桥.
所以,一个贪心算法应运而生.
构造模型
所以,我们考虑最快的两个人A1,A2(下称A,B)和最慢的两个人An,An-1(下称a,b).
我们有两种方案:
- 用A传递手电筒帮助a,b过桥.
先让A,a过桥,用时为a,A再回来,用时为A,再让A,b过桥,用时为b,A再回来,用时为A.
总用时为a+b+2*A - 用A,B传递手电筒帮助a,b过桥.
先让A,B过桥,用时为B,再让A回来,用时为A,再让a,b过桥,用时为a,B再回来,用时为B.
总用时为a+A+2*B
我们比较这两种方案,取最优(最小)方案即可.
这样,我们每次可以让两个人以最优策略过桥.
边界条件
通过上述方法,我们每次能将人员个数减少2.
那么,什么时候可以停止呢?只有一个人的时候停止显然是不够的.
经过分析,可以得到:
- 2个人是合法的终止条件,因为两个人可以一次过桥,属于合法条件.
- 3个人是合法的终止条件,因为贪心策略里可以省略最后一步,以达到3人过桥的效果.
所以,
当剩余2个人时,时间为最慢的那一个(A2),时间为A2.
当剩余3个人时,让最快的人(A1)和最慢的人(A3)过桥,A1再回来,再与A2过桥,总时间为A1+A2+A3.
这样,我们就可以正式写出代码了.
注意事项
- 题目要求输出方案,可以用一个V数组记录.
vector会更方便,但是push_back() 比较丑长,容易污染代码出错.
不妨用V[0]记录这次过桥是1个人还是2个人. - 当n=1时,算法没有关于这组数据的阐述,可以加一组特判.
// 可怜我在这上面 WA了好几次 ?
不理解的地方可以看代码.
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int N=1010;
int n;
int a[N];
int v[2*N][3];
int main()
{
int cnt=0;
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
//n=1时的特判
if (n==1)
{
printf("%d\n%d",a[1],a[1]);
return 0;
}
sort(a+1,a+n+1);
int ans=0,i;
for (i=n;i>3;i-=2)
{
//if 是贪心抉择
if (a[i-1]+a[1]<2*a[2])
{
//A[i-1,i,1]的方案1
ans+=a[i-1]+a[i]+2*a[1];
//V数组记录方案
v[++cnt][0]=2,v[cnt][1]=a[1],v[cnt][2]=a[i-1];
v[++cnt][0]=1,v[cnt][1]=a[1];
v[++cnt][0]=2,v[cnt][1]=a[1],v[cnt][2]=a[i];
v[++cnt][0]=1,v[cnt][1]=a[1];
}
else
{
//A[1,2,i]的方案2
ans+=a[1]+a[i]+2*a[2];
//V数组记录方案
v[++cnt][0]=2,v[cnt][1]=a[1],v[cnt][2]=a[2];
v[++cnt][0]=1,v[cnt][1]=a[1];
v[++cnt][0]=2,v[cnt][1]=a[i-1],v[cnt][2]=a[i];
v[++cnt][0]=1,v[cnt][1]=a[2];
}
}
//边界条件判定
if (i==3)//有三个人,分别为A[1,2,3]
{
ans+=a[1]+a[2]+a[3];
v[++cnt][0]=2,v[cnt][1]=a[1],v[cnt][2]=a[3];
v[++cnt][0]=1,v[cnt][1]=a[1];
v[++cnt][0]=2,v[cnt][1]=a[1],v[cnt][2]=a[2];
}
if (i==2)//有两个人,分别为A[1,2]
{
ans+=a[2];
v[++cnt][0]=2,v[cnt][1]=a[1],v[cnt][2]=a[2];
}
//输出方案
printf("%d\n",ans);
for (int i=1;i<=cnt;i++)
{
for (int j=1;j<=v[i][0];j++)
printf("%d ",v[i][j]);
puts("");
}
return 0;
}
后记
本题主要考察贪心和思维宽度.
只使用贪心可能不足以解决本题,还需稍加思考.
与其说是贪心,不如说是IQ 1000题.
感谢奆老关注 qwq ?