信息学奥赛一本通 1319:【例6.1】排队接水 | 洛谷 P1223 排队接水

【题目链接】

ybt 1319:【例6.1】排队接水
洛谷 P1223 排队接水

【题目考点】

1. 贪心

2. 贪心选择性质的证明

要想证明贪心选择可以得到最优解,只需要证明最优解包含每一次的贪心选择。
使用数学归纳法:

  1. 证明最优解包含第一次的贪心选择
  2. 假设存在最优解包含前k次的贪心选择,证明该最优解包含第k+1次的贪心选择

【解题思路】

1. 证明贪心选择性质

贪心选择:每次选择接水时间最短的人去接水
贪心选择性质的证明:
设n个人的编号为1,2,3,…,n

  1. 证明:存在最优解,第一个接水的人是通过贪心选择得到的。
    接水时长最短的人编号为 g g g,其接水时间为 T g T_g Tg

使用反证法:假设对于所有最优解,第一个接水的人都不可以是贪心选择得到的人g。
则对于某种最优解,其接水顺序为 d 1 , d 2 , . . . , d n d_1,d_2,...,d_n d1,d2,...,dn,假设g是第m个接水的人,即 d m = g ( m > 1 ) d_m = g (m > 1) dm=g(m>1),记第1个接水的人的编号为a,即 d 1 = a d_1 = a d1=a
由于g的所有人中打水时间最短的人,那么一定有 T a > T g T_{a} > T_{g} Ta>Tg
如果交换 a a a g g g,考虑总接水时间的变化:

  • d 2 ∼ d m − 1 d_2\sim d_{m-1} d2dm1一共 m − 2 m-2 m2个人,每人等待的时间减少了 T a T_a Ta增加了 T g T_g Tg,总减少时间为 ( m − 2 ) ( T a − T g ) (m-2)(T_a-T_g) (m2)(TaTg)
  • g等待的时间从 T a + ∑ i = 2 m − 1 T d i T_a+\sum_{i=2}^{m-1}T_{di} Ta+i=2m1Tdi变为 0 0 0,等待时间减少 T a + ∑ i = 2 m − 1 T d i T_a+\sum_{i=2}^{m-1}T_{di} Ta+i=2m1Tdi
  • a的等待时间从 0 0 0变为 T g + ∑ i = 2 m − 1 T d i T_g+\sum_{i=2}^{m-1}T_{di} Tg+i=2m1Tdi,等待时间减少 − ( T g + ∑ i = 2 m − 1 T d i ) -(T_g+\sum_{i=2}^{m-1}T_{di}) (Tg+i=2m1Tdi)
  • 总减少的等待时间为上面三者的加和,结果为 ( m − 1 ) ( T a − T g ) (m-1)(T_a-T_g) (m1)(TaTg)。由于 m > 1 m>1 m>1 T a > T b T_a>T_b Ta>Tb,那么 ( m − 1 ) ( T a − T g ) > 0 (m-1)(T_a-T_g) > 0 (m1)(TaTg)>0

因此,如果交换a与g,总接水时间会减少。这与当前解是可以使平均接水时间最小的最优解相矛盾,因而原命题得证。

  1. 证明:存在最优解,假设前k个人接水的人都是通过贪心选择得到的,第k+1个接水的人也通过贪心选择得到,即选择剩下的接水时长最短的人。

证明方法与上面第1点的证明方法类似,把证明中的“第一个接水的人”改为“第k+1个接水的人”即可,不再赘述。

以上证明了该问题具有贪心选择性质,即每次选择接水时间最短的人去接水,可以使n个人的平均等待时间最小。

2. 具体做法

结构体中保存人的编号和打水时间,按打水时间升序对结构体对象进行排序。计算所有人的等待时间加和,最后除以总人数。

3. 排序方法

该题说当时间重复时,按照输入顺序输出。虽然它说“用sort是可以的”,但严格来讲,这里按照输入顺序将其加入数组中,而后按照打水时间进行排序。如要让相同打水时间的元素保持输入时的先后顺序,则理论上必须选用稳定的排序算法。所以这里我们使用stable_sort进行排序。

【题解代码】

解法1:贪心,使用stable_sort进行排序

#include
using namespace std;
#define N 1005
struct Time
{
    double t;
    int i;//t:时间 i:人的编号
};
Time a[N];
bool cmp(const Time &a, const Time &b)
{
    return a.t < b.t; 
}
int main()
{
    int n;
    double sumTime = 0, waitTime = 0;
    cin >> n;
    for(int i = 1; i <= n; ++i)
    {
        cin >> a[i].t;
        a[i].i = i;
    }
    stable_sort(a+1, a+1+n, cmp);
    for(int i = 1; i <= n; ++i)
    {
        sumTime += waitTime;//waitTime:当前这个人已经等的时间 
        waitTime += a[i].t;
        cout << a[i].i << ' ';
    }
    cout << endl << fixed << setprecision(2) << sumTime / n; 
    return 0;
}

解法2:贪心 手写归并排序

#include
using namespace std;
#define N 1005
struct Time
{
    double t;
    int i;//t:时间 i:第几个 
};
Time a[N], t[N];
void mergeSort(int l, int r)//归并排序 
{
    if(l >= r)
        return;
    int mid = (l+r)/2;
    mergeSort(l, mid);
    mergeSort(mid+1, r);
    int i = l, j = mid+1, ti = l;
    while(i <= mid && j <= r)
    {
        if(a[i].t < a[j].t)
            t[ti++] = a[i++];
        else
            t[ti++] = a[j++];
    }
    while(i <= mid)
        t[ti++] = a[i++];
    while(j <= r)
        t[ti++] = a[j++];
    for(int k = l; k <= r; ++k)
        a[k] = t[k];
}
int main()
{
    int n;
    double sumTime = 0, waitTime = 0;
    cin >> n;
    for(int i = 1; i <= n; ++i)
    {
        cin >> a[i].t;
        a[i].i = i;
    }
    mergeSort(1, n);
    for(int i = 1; i <= n; ++i)
    {
        sumTime += waitTime;//waitTime:当前这个人已经等的时间 
        waitTime += a[i].t;
        cout << a[i].i << ' ';
    }
    cout << endl << fixed << setprecision(2) << sumTime / n; 
    return 0;
}

你可能感兴趣的:(信息学奥赛一本通题解,洛谷题解,c++)