水库蓄水问题【C语言】

/*
【双指针】
公元2919年,人类终于发现了一颗宜居星球——X星。
现想在X星一片连绵起伏的山脉间建一个天热蓄水库,如何选取水库边界,使蓄水量最大?要求:
山脉用正整数数组s表示,每个元素代表山脉的高度。
选取山脉上两个点作为蓄水库的边界,则边界内的区域可以蓄水,蓄水量需排除山脉占用的空间蓄水量的高度为两边界的最小值。
如果出现多个满足条件的边界,应选取距离最近的一组边界。
输出边界下标(从0开始)和最大蓄水量;如果无法蓄水,则返回0,此时不返回边界。
例如,当山脉为s=[3,1.2]时,则选取s[0]和s[2]作为水库边界,则蓄水量为1,此时输出:02:1
当山脉s=[3,2,1]时,不存在合理的边界,此时输出:0。
给定一个长度为n的整数数组height。数组的元素表示山的高度,选择两个元素作为水库的边界,
求蓄水量的最大值并输出蓄水量最大时的边界下标(蓄水量相同时输出下标较近的)。
输入描述:
输入一行数字,空格分隔。输出描述:
输出蓄水量的最大值及输出蓄水量最大时的边界下标示例1:
输入:
1 8 6 2 5 4 8 3 7
输出:
1 6:15
说明:
蓄水量的最大值为15
蓄水量最大时的边界下标为1和6
*/

#include <stdio.h>

#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))

int main(void) {
    int height[10000];
    int n = 0;//n表示输入数字个数
    while (1) {
        scanf("%d", &height[n]);
        n++;
        if(getchar() == '\n'){
            break;
        }
    }
    int left = 0;
    int right = n - 1;
    int result_left = 0, result_right = 0, result_sum = 0 ;
    // int capacity = 0;//中间变量,用来保存sum & result_sum的值,用于下次循环的判断(sum每次会被重置)

    while (left < right){//左右指针碰到就停
        int sum = 0; //sum表示蓄水量,在循环内部定义,每次循环结束都会归零
        int lower = MIN(height[left], height[right]);//找到左右两边较矮峰

        for ( int i = left; i <= right; i++) {
            sum = sum + MAX(0, lower - height[i]);//只有比左右两边较矮峰更矮的峰才能蓄水
        }
        
        if(sum >= result_sum){//一定要有等于号,因为题目要求蓄水量相同时输出下标较近的
            result_left = left;
            result_right = right;
            result_sum = sum;
        }

            if (height[left] < height[right]) { // h[L]是矮峰
            //此时我们不应该直接L++,而是需要检查h[R-1] 是否不低于h[R],如果不低于,则应该R--,因为这样可以在不减少最大蓄水量的同时,减小两峰距离。
                if (height[right - 1] >= height[right]) 
                    right--; 
                else 
                    left++; // 否则L++
            } 
            else if (height[left] > height[right]) { // 原理同上
                if (height[left + 1] >= height[left]) 
                    left++;
                else 
                    right--;
            } 
            else { // 如果两峰高度相同
                if (height[left + 1] > height[right - 1]) 
                    left++;
                else 
                    right--;
            }
        
        //-----------------------------------------------------------------------------------
        //右-1比右和左都长,右--,左-1比左和右都长,左--
        // if (height[right - 1] >= height[left] && height[right - 1] >= height[right]) 
        //     right--;
        // else if (height[left + 1] >= height[right] && height[left + 1] >= height[left]) 
        //     left++;
        
        // //只有短板内移才有可能变大,长板内移动肯定变小,所以左右谁短谁内移
        // else if (height[left] < height[right]) 
        //     left++;
        // else if (height[left] > height[right]) 
        //     right--;
        // else 
        //     right--;
        //-----------------------------------------------------------------------------------
        //上面的判断条件部分有重叠,但应该也可以
    }

    //输出结果
    if(result_sum == 0){
        printf("0");
    }
    else {
        printf("%d %d:%d",result_left, result_right, result_sum );
    }
    
    return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值