算法竞赛学习日记(1)——“贪心法”

算法是不可能单纯的以方法来概括的,我觉得概括的只是一种人的基本想法而已。对于我这样的算法初学者,在碰到算法编程题的时候不知如何下手,只是有想法寥寥,而不知道应该以何种方法去做下去,所以手足无措。

贪心法——只顾眼前的利益,反而得到了最优的解。

这本来就是每个人在拿到一个算法题的时候会有的想法,所以归类方法的意义不在于身兼百种方法去一一与题目配对,而在于从一种想法入手,坚持尝试下去,从而真正的尝试出这种想法是否真正可行,而不要将想法一闪而过然后不知所措。

在《算法竞赛入门经典》这本书中,关于贪心法的乘船问题中,要求在每条船都有重量和人数限制的前提下,追求使用船的最少化问题。 同样是利用sort算法将人按照重量的大小来从小到大排序。

书中分析法我觉得自己很受用。我想的是最重的人要与之匹配能够乘船的重量最重的人绑定

这样的话我们将最重的人标记为i,将与i绑定的人的重量标记为j
用反证法来证明这种匹配方法是否为最优解

分两种情况:
1. i只自己坐一艘船,那么当i和j同坐一艘船的时候一定不会增加船的数量而且可能使其数量减少。
2. i与非j的另一人k同坐一船,k的重量一定比j要轻, 那么此时将j和k交换,这时候k所在的船不会超重,而i和j配对也不会超重,那么就不会增加船的数量,所以这种配对方法不会丢掉最优解….吗?

i只有这三种情况吗?
自己坐、与j坐、与j以外的另一个k坐。我会怀疑,会不会有这种情况的发生——有两只重量本来不算太重的船没有办法配对,而这些船也能够与j配对呢? 很可能会有,但这却不影响这道题的结果。 所以对于最后以船的数量作为一个指标,确实可以用这三种情况来框定,所以这样得到的解确实是最优解的一种。

然后我想这个算法要怎么来编写呢?
将重量数组从大到小排序,在遍历的时候,我们将当下最重的标记为i,与之可以配对的最重的人标记为j,配对之后,i++,那么j呢?
此时的J肯定要比之前的重,因为i轻了,那么对j就要执行j–喽?
然后直到i和j相碰的时候再返回去再去为i找第一次找到的j的地方之后去找更轻的?
哇,那这个编程真的有点麻烦了。
所以我觉得将sort算法的谓词设置为lessthan是有一定的道理的,有时候需要去按照原定的意思,于是有了书中的解法,为最轻的人i去配对与之可以配对的最重的人。
同样是反证法,而在遍历的时候要轻松的多,当i++之后,j比原来的j要轻了,所以j–,但是这时候的逻辑要清晰很多,那就是剩下的都好重啊,那就让他们一人坐一艘船呗。

int main()
{
    int n(0);
    scanf_s("%d\n", &n);
    const int max = 20000;
    int a[max] = {};
    for (int i(0); i < n; i++)
        scanf_s("%d", &a[i]);              
    sort(a, a + n);
    int k = n - 1;
    int total(0);
    int pairs(0);
    for (; k > 0; k--)
    {
        if ((a[0] + a[k]) <= 100)
        {
            pairs++;
            break;
        }
    }   
    for (int i(1); i < k; i++)
   {
     for (int j(k - 1); j > i; j--)
     {
       if ((a[i] + a[j]) <= 100)
        {
         k = j;
         pairs++;
         break;  // 这里我在编写的时候一直在想,如果针对某个i我找不到与之配对的j应该怎么跳出循环,后来我觉得我的思维又有点不机器化了。找不到也让它循环着,不动 k 也不动 pairs也照样能计算清楚
        }
     }
    }
    total = n - pairs;
    printf("The number of  ships is %d\n", total);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值