算法-贪心算法-二分查找 1、网易2017内推C++笔试题目

1、网易2017内推C++笔试题目

https://blog.csdn.net/damotiansheng/article/details/52160496

2、贪心算法

https://blog.csdn.net/thesnowboy_2/article/details/73385652

题目描述

牛牛和 15个朋友来玩打土豪分田地的游戏,牛牛决定让你来分田地,地主的田地可以看成是一个矩形,每个位置有一个价值。分割田地的方法是横竖各切三刀,分成 16份,作为领导干部,牛牛总是会选择其中总价值最小的一份田地, 作为牛牛最好的朋友,你希望牛牛取得的田地的价值和尽可能大,你知道这个值最大可以是多少吗?

输入描述:

每个输入包含 1 个测试用例。每个测试用例的第一行包含两个整数 n 和 m(1 <= n, m <= 75),表示田地的大小,接下来的 n 行,每行包含 m 个 0-9 之间的数字,表示每块位置的价值。

输出描述:

输出一行表示牛牛所能取得的最大的价值。

参考思路:

链接:https://www.nowcoder.com/questionTerminal/fe30a13b5fb84b339cb6cb3f70dca699

来源:牛客网

题意是:通过横竖各三刀将一个矩阵分为16部分,每部分的value即为这一部分包含的所有数字之和。我们要做的就是想一种切法,使得这16部分中value最小的那个尽可能的大。首先很显然,每一个部分的value在0-sum之间,sum是指整个矩阵所有数字之和。这样最终的结果一定是[0, sum]中的某一个整数。
这里稍微逆向思考一下,既然不容易直接求结果,可不可以我 猜一个值(k),然后判断能不能 通过某种切法使最小的那一块value>=k呢?(也就是说,使16块的value都能大于等于k)
如果可以的话,我们就可以 对[0, sum]这个区间进行二分查找。这个容易理解吧,当然逻辑上你从num开始递减遍历判断a肯定也是ok的,但是会超时,所以换成二分,二分的复杂度是log(sum)
接下来是重点:对于一个值,怎么判断能不能横竖切三刀,使16块的value都大于等于k呢
可以先横着切三刀,然后纵向贪心遍历一遍。这部分我也不用多说了,想不通的话看看代码就明白了。复杂度是 n^4

思路: 用sum数组存储到(0,0)到(i,j)的面积和,(n,m)上可以获得最大面积 面积区间是[0,sum[n][m]] 在这个区间上取平均数k完成多次的二分查找 循环条件是下限<=上限 每次获得一个k 判定是否满足竖切三刀情况下 能够获得至少一次满足 横向贪心算法遍历一遍。
#include <cstdio>
#include <cstring>
#include<cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
int n,m;
char str[100];
int a[110][110]; //初始化输入价值
int sum[110][110];

int ct(int x, int y, int i, int j) //从[x,y]到[i,j]中间面积和
{
    return sum[i][j] - sum[x][j] - sum[i][y] + sum[x][y]; //此处是需要将x,y以及之上面积刨除的 有一道题是需要算上x,y的 - -
}

bool judge(int r)
{
    for(int i=1; i<=m-3; i++)
    {
        for(int j=i+1; j<=m-2; j++)
        {
            for(int k=j+1; k<=m-1; k++)
            {
                int cur = 0,cnt = 0; //cur 上一行   count 上限四 代表 横切刀数+1
                for(int h = 1; h <= n; h++)
                {
                    int part1 = ct(cur, 0, h, i);   //在横切为第cur行时 获得的四份 共16份
                    int part2 = ct(cur, i, h, j);
                    int part3 = ct(cur, j, h, k);
                    int part4 = ct(cur, k, h, m);
                    if(part1 >= r && part2 >= r && part3 >= r && part4 >= r)
                    {
                        cnt++;
                        cur = h; //代表上一行
                    }
                }
                if(cnt >= 4) //横切方式 >= 3
                    return true;
            }
        }
    }
    return false;
}
int main(){
    memset(sum, 0, sizeof sum);
    //freopen("input.txt","r",stdin);
    while(cin>>n>>m)
    {
        for(int i=1;i<=n;i++)
        {
            cin>>str+1;
            for(int j=1; j<=m; j++)
            {
                a[i][j] = str[j] -'0';
                sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + a[i][j]; //左上角到[i,j]的价值总和
            }
        }
        //猜测最小的最大价值和
        int k=0, M=sum[n][m], result=0;
        while(k<=M)//二分法找到最小的最大和
        {
            int r = (M + k)>>1;
            if(judge(r))
            {
                result = r;
                k = r + 1;
            }
            else
                M = r - 1;
        }
        //freopen("CON","r",stdin);
        cout<<result<<endl;
    }
    //system("pause");
    return 0;
}







猜你喜欢

转载自blog.csdn.net/yanbao4070/article/details/80940889