基础算法之贪心法

贪心法:遵循某种规律,不断贪心的选取当前最优策略的算法设计方法。

例如:有1元、5元、10元,20元,100元,200的钞票无穷多张。现在使用这些钞票支付X元,最少需要多少张?

思路:尽量用面额较大的金额,所以可以将钞票排一下顺序,然后计算出每个大额钞票最多能用几张,依次算出张数进行叠加

#include<stdio.h>

int main()
{
    int RMB[]={200,100,20,10,5,1};
    int NUM=6;
    int X=628;
    int count_RMB=0;
    for(int i = 0;i<NUM;i++)
    {
        int use = X/RMB[i];
        count_RMB+=use;
        X=X-use*RMB[i];
        printf("需要面额为%d的%d张,",RMB[i],use);
        printf("剩余需要支付金额%d.\n",X);
    }
    printf("总共需要%d张\n",count_RMB);
    return 0;
}

LEECODE题目练习

455:分饼干问题

设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

注意:

你可以假设胃口值为正。

一个小朋友最多只能拥有一块饼干。

示例 1:

输入: [1,2,3], [1,1]

输出: 1

解释:

你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3

虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。

所以你应该输出1

示例 2:

输入: [1,2], [1,2,3]

输出: 2

解释:

你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2

你拥有的饼干数量和尺寸都足以让所有孩子满足。

所以你应该输出2.

解法:

分析:1.当某个孩子可以被多个饼干满足时,是否需要优先用某个饼干满足这个孩子?

2.当某个饼干可以满足多个孩子时,是否需要优先满足某个孩子?

思路:

1.首先对需求因子数组g和饼干大小S进行从小到大的排序

2.按照从小到大的顺序使用各个饼干尝试是否可以满足某个孩子,每个饼干只尝试1次,若尝试成功,则换下一个孩子尝试;直到发现没更多的孩子或者没更多的饼干,循环结束。

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        std::sort(g.begin(),g.end());
        std::sort(s.begin(),s.end());
        int child = 0;
        int cookie = 0;
        while(child<g.size()&&cookie<s.size()){//当孩子或饼干同时均未尝试完时
            if(g[child]<=s[cookie]){
                child++; //该糖果满足了孩子,孩子指针向后移动
            }
            cookie++; //无论成功或失败,每个糖果只尝试一次,cookie向后移动;
        }
        return child;
    }
};

测试程序
int main()
{
    Solution solve;
    std::vector<int> g;
    std::vector<int> s;
    g.push_back(5);
    g.push_back(10);
    g.push_back(2);
    g.push_back(9);
    g.push_back(15);
    g.push_back(9);
    s.push_back(6);
    s.push_back(1);
    s.push_back(20);
    s.push_back(3);
    s.push_back(8);
    printf("%d\n",solve.findContentChildren(g,s));
    return 0;
}

输出:

https://pic4.zhimg.com/80/v2-c8874554428150c7a6a5f159587245a2_1440w.jpg

376:摇摆序列

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。

例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。

示例 1: 输入: [1,7,4,9,2,5] 输出: 6

解释: 整个序列均为摆动序列。

示例 2: 输入: [1,17,5,10,13,15,10,5,16,8] 输出: 7

解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]

示例 3: 输入: [1,2,3,4,5,6,7,8,9] 输出: 2

贪心规律:

当序列有一段连续的递增(或递减时,为形成摇摆子序列,我们只需要保留这段连续的递增(或递减)的首尾元素,这样更可能使得尾部的后一个元素成为摇摆子序列的下一个元素。

https://pic4.zhimg.com/80/v2-91c691dd89b62847fc13fc5054cf34ab_1440w.jpg

class Solution {
public:
    int wiggleMaxLength(std::vector<int>& nums) {
        if(nums.size()<2)
        {
            return nums.size();
        }
        static const int BEGIN = 0;
        static const int UP=1;
        static const int DOWN=2;
        int STATE=BEGIN;
        int max_length = 1;

        for(int i = 1;i<nums.size();i++)
        {
            switch(STATE){
            case BEGIN:
            if(nums[i-1]<nums[i]){
                STATE=UP;
                max_length++;
            }
            else if(nums[i-1]>nums[i])
            {
                STATE=DOWN;
                max_length++;
            }
            break;
            case UP:
            if(nums[i-1]>nums[i])
            {
                STATE=DOWN;
                max_length++;
            }
            break;
            case DOWN:
            if(nums[i-1]<nums[i])
            {
                STATE=UP;
                max_length++;
            }
            break;
        }
    }
    return max_length;
    }
};

402:移除K个数字

给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。

注意: num 的长度小于 10002 ≥ k。 num 不会包含任何前导零。

示例 1 :

输入: num = "1432219", k = 3  输出: "1219"

解释: 移除掉三个数字 4, 3, 2 形成一个新的最小的数字 1219

示例 2 :

输入: num = "10200", k = 1 输出: "200"

解释: 移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。

示例 3 : 输入: num = "10", k = 2 输出: "0"

解释: 从原数字移除所有的数字,剩余为空就是0

思路:利用贪心算法找出最优解,依次将字符压入栈,若num[i]<top,则弹出栈 k--;

难点

1.当遍历完所有字符后,k>0怎么办?说明前面都不满足“比后面数字大”的条件,入栈的都是想等或者比后继小的数,依次弹出栈直到k=0;

2.当字符为‘0’时如何处理?如果此刻栈中没有元素(因为比当前的0大),说明0被挪到首位,则continue,不用入栈;如果栈中有元素,这说明0位于整数后,则push;

class Solution {
public:
    string removeKdigits(string num, int k) {
        std::vector<int> SS;
        std::string result="";
        SS.push_back(num[0]-'0');
        for(int i = 1;i<num.length();i++)
        {
            int number = num[i]-'0';
            while(SS.size()!=0 && SS[SS.size() - 1] > number && k > 0)
            {
                SS.pop_back();
                k--;
            }
            if(number==0)
            {
                if(SS.size()==0)
                {
                    continue;
                }
                else
                {
                    SS.push_back(number);
                }
            }
            else
            {
                SS.push_back(number);
            }
        }
        while(SS.size()!=0 && k>0)
        {
            SS.pop_back();
            k--;
        }
        for(int i =0;i<SS.size();i++)
        {
            result.append(1,SS[i]+'0');
        }
        if(result=="")
        {
            result="0";
        }
        return result;
    }
};

55.跳跃游戏

给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。

示例 1: 输入: [2,3,1,1,4] 输出: true

解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 3 步到达最后一个位置。

示例 2: 输入: [3,2,1,0,4] 输出: false

解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 所以你永远不可能到达最后一个位置。

算法思路:

1.求从第i位置最远可跳至第index[i]位置,根据从第i位置最远可跳nums[i]步:index[i]=nums[i]+i;

2.初始化:

1)设置变量jump代表当前所处的位置,初始化为0

2)设置变量maxindex代表从第0位置至第jump位置这个过程中最远可到达的位置,初始化为max_index[0]

3利用jump扫描index数组,直到jump达到index数组尾部或超过max_index,扫描过程中,更新max_index

4若最终jump为数组长度,则返回true,否则返回false

class Solution {
public:
    bool canJump(vector<int>& nums) {
        std::vector<int> index;
        for(int i =0;i<nums.size();i++)
        {
            index.push_back(i+nums[i]);
        }
        int max_index=index[0];//max_index是jump在最多可以走的步数;
        int jump = 0; //超级马里奥
        while(jump<index.size()&&jump<=max_index) //如果max_index=nums.size-1;说明jump是可以走到底的;
        {
            if(max_index<index[jump])
            {
                max_index=index[jump];
            }
            jump++;
        }
        if(jump==index.size())
        {
            return true;
        }
        return false;

    }
};

45跳跃游戏2

给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例:

输入: [2,3,1,1,4]

输出: 2

解释: 跳到最后一个位置的最小跳跃数是 2

从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

这道题的最优解是可以时间复杂度优化到 O(n) 的,那就是采用贪心算法,我们从左边的起点开始跳跃的时候,我们应该跳跃到哪一个点比较合适呢?,显然,每次都跳跃最大长度的话,是不行的。例如对于上面 arr = {2, 3, 1, 1, 4, 2, 1} 这个例子,刚开 arr[0] = 2,那么我们可以跳到 arr[1] = 3 或者 arr[2] = 1 上,显然,我们跳跃 arr[1] = 3 会更好一点。如图(图片来源于网络)

接着同样的道理,我们可以从 arr[1] = 3 这个位置开始跳跃,它可以跳跃到 arr[2] = 1, arr[3] = 1, arr[4] = 4 这三个位置,显然,我们跳到 arr[4] = 4 这个位置好一点,如图(图片来源于网络)

也就是说,我们要跳跃的那个点,可以使得上一次 + 下一次的跳跃总距离最远。代码如下

 

int jump(vector<int> nums) {
        if(nums.size() < 2)
            return 0;

        int sum = 0;
        int end = 0; // 能跳到的最远距离
        int max = 0; // 下一步可以跳到的最远距离
        for(int i = 0; i < nums.size() - 1; i++){
            max = max(max, i + nums[i]);
            // 更新当前点
            if(i == end){
                end = max;
                sum++;
            }
        }
        return sum;
    }

452射击气球

在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在104个气球。

一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstartxend 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。

https://pic2.zhimg.com/80/v2-e0395b36e4721bf02ec686f4e496061d_1440w.jpg

Example:

输入:

[[10,16], [2,8], [1,6], [7,12]]

输出:2

解释:

对于该样例,我们可以在x = 6(射爆[2,8],[1,6]两个气球)和 x = 11(射爆另外两个气球)。

1.对于某个气球,至少需要使用1个弓箭将他、它击穿;

2.在这只气球将其击穿的同时,尽可能击穿其他更多的气球!

 

思路:

1.对各个气球进行排序,按照气球的左端点从小到大排序。

2.遍历气球数组,同时维护一个射击区间,在满足可以将当前气球射穿的情况下,尽可能击穿更多的气球,每击穿一个新的气球,更新一次射击区间

3.如果新的气球没办法被击穿了,则需要一名弓箭手,即维护一个新的射击区间,随后,遍历气球数组

利用vector实现

bool cmp(std::vector<int> &a,std::vector<int> &b)
{
    return a[0]<b[0];
}

class Solution {
public:
    int findMinArrowShots(vector<vector<int>>& points) {
        if(points.size()==0)
        {
            return 0;
        }
        std::sort(points.begin(),points.end(),cmp);
        int shot_num=1;
        int shot_begin=points[0][0];
        int shot_end=points[0][1];
        for(int i=0;i<points.size();i++)
        {
            if(points[i][0]<=shot_end)
            {
                shot_begin=points[i][0];
                if(points[i][1]<shot_end)
                {
                    shot_end=points[i][1];
                }
            }
            else
            {
                shot_num++;
                shot_begin=points[i][0];
                shot_end=points[i][1];
            }
        }
    return shot_num;
    }
};

利用pair实现

#include<algorithm>

#include <vector>

#include<stdio.h>

bool cmp (const std::pair<int,int> &a,const std::pair<int,int> &b)
//pair是将2个数据组合成一组数据,当需要这样的需求时就可以使用pair,
{
    return a.first<b.first; //不必考虑左端点相同的排序;
}

class Solution {
public:
    int findMinArrowShots(std::vector<std::pair<int,int> >& points) {
        if(points.size()==0)
        {
            return 0;
        }
        std::sort(points.begin(),points.end(),cmp);

        int shoot_num=1;
        int shoot_begin=points[0].first;
        int shoot_end = points[0].second;

        for(int i=1;i<points.size();i++)
        {
            if(shoot_end>=points[i].first)
            {
                shoot_begin=points[i].first;
                if(shoot_end>points[i].second)
                {
                    shoot_end=points[i].second;
                }
            }
            else{
                shoot_num++;
                shoot_begin = points[i].first;
                shoot_end = points[i].second;
            }
        }
        return shoot_num;
    }
};

int main()
{
    std::vector<std::pair<int,int>> points;
    points.push_back(std::make_pair(10,16));
    points.push_back(std::make_pair(2,8));
    points.push_back(std::make_pair(1,6));
    points.push_back(std::make_pair(7,12));
    Solution solve;
    printf("%d\n",solve.findMinArrowShots(points));
    return 0;
}

附加题目:

poj2431:最优加油方法

一条公路上,有一个起点和一个终点,之前有n个加油站;已知:

1.加油站到终点的距离d和各个加油站可以加油的量l;

2.起点位置到终点距离为L,起始油箱油量为P;

假设一个单位汽油能走一个单位距离,油箱没有上线,最少加几次有可以从起点开到终点?(无法到达终点,返回-1)

https://picb.zhimg.com/80/v2-fc3d757fa807a84f7977e314a3d0ddc1_1440w.jpg

思考:何时加油?油用光时,在哪个站加油?油量最多的加油站最合适

利用最大堆来存储油量;

思路:

1.设置一个最大堆,用来存储经过加油站的汽油量;

2.按照起点到终点的方向,遍历各个加油站之间的距离

3.每次需要走两个加油站之间的距离d,如果发现汽油不够走距离d时,从最大堆中取出一个油量添加,知道可以足够走距离d

4.如果把最大堆的汽油添加仍然不够行进距离d,则无法达到终点;

5.当前油量P减少d;

6.将当前加油站油量添加至最大堆

#include<stdio.h>

#include<vector>

#include<algorithm>

#include<queue>

bool cmp(const std::pair<int,int> &a,const std::pair<int,int> &b){

    return a.first>b.first;

}

int get_minmum_stop(int L,int P, std::vector<std::pair<int,int>> &stop)

/*L:起点到终点的距离

P起点初始的油量

stop:pair<加油站至终点的距离,加油站汽油箱>

*/

{

    std::priority_queue<int> Q; //存储油量的最大值

    int result = 0;//记录加过几次油的变量

    stop.push_back(std::make_pair(0,0));//将终点作为一个停靠点,添加进stop;

    std::sort(stop.begin(),stop.end(),cmp);//按照距离终点的距离排序

    for(int i = 0; i<stop.size();i++)

    {

        int dis = L-stop[i].first;

        while(!Q.empty() && P<dis)

        {

            P+=Q.top();

            Q.pop();

            result++;

        }

        if(Q.empty()&&P<dis)

        {

            return -1;

        }

        P = P-dis;

        L=stop[i].first;

        Q.push(stop[i].second);

    }

    return result;

}

 

int main()

{

    std::vector<std::pair<int,int>> stop;

    int N;

    int L;

    int P;

    int distance;

    int fuel;

    scanf("%d",&N);

    for(int i=0;i<N;i++)

    {

        scanf("%d %d",&distance,&fuel);

        stop.push_back(std::make_pair(distance,fuel));

    }

    scanf("%d %d",&L,&P);

    printf("%d\n",get_minmum_stop(L,P,stop));

    return 0;

}

输出:

https://pic4.zhimg.com/80/v2-9d3d384560a85fe7589c2ef722eff3f5_1440w.jpg

 

预备知识:

图的表示与构造

使用map构造邻接表表示的图,map定义为以stringkey(代表图的顶点),vector<string>value(代表图的各个顶点临接的顶点)。

beginWord push进入wordList。遍历wordList,对任意两个单词wordList[i]wordList[j],wordList[i]wordList[j]只相差1个字符,则将其相连。

bool connect(const std::string &word1,const std::string &word2)

{

int cnt =0;

for(int i =0 ;i<word1.length();i++)

{

   if(word1[i]!=word2[i])

   {

       cnt++;

   }

}

return cnt ==1;

}

 

void construct_graph(std::string &beginWord,

                     std::vector<std::string>& wordList,

                     std::map<std::string,std::vector<std::string>> &graph)

                     {

                         wordList.push_back(beginWord);

                         for(int i = 0;i<wordList.size();i++)

                         {

                             graph[wordList[i]]=std::vector<std::string> ();

                         }

                         for(int i = 0;i<wordList.size();i++)

                         {

                             for(int j = i+1;j<wordList.size();j++)

                             {

                                 if(connect(wordList[i],wordList[j]))

                                 {

                                     graph[wordList[i]].push_back(wordList[j]);

                                     graph[wordList[j]].push_back(wordList[i]);

                                 }

                             }

                         }

                     }

预备知识2 图的宽度遍历

给定图的起点beginWord,终点endWord,graph,从beginWord开始宽度优先搜索图graph,搜索过程中记录到达步数;

1.设置搜索队列Q,队列节点为pair<顶点,步数>;设置集合visit,记录搜素过得顶点,将<beginWord,1>添加至队列;

2.只有队列不空,取出队列头部元素:

1)若取出的队列头部元素为endWord,返回到达当前节点的步数;

2)否则拓展该节点,将与该节点相邻的且未添加到visit中的节点与步数同时添加至队列Q,并将拓展节点加入visit;

3.若最终都无法搜索到endWord,返回0

int BFS_graph(std::string &beginWord,std::string &endWord,

                  std::map<std::string,std::vector<std::string>> &graph)

                  {

                      std::queue<std::pair<std::string,int>> Q;

                      std::set<std::string> visit;

                      Q.push(std::make_pair(beginWord,1));

                      visit.insert(beginWord);

                      while(!Q.empty())

                      {

                          std::string node = Q.front().first;

                          int step = Q.front().second;

                          Q.pop();

                          if(node==endWord)

                          {

                              return step;

                          }

                          const std::vector<std::string> &neighbors = graph[node];

                          for(int i = 0;i<neighbors.size();i++)

                          {

                              if(visit.find(neighbors[i])==visit.end())

                              {

                                  Q.push(std::make_pair(neighbors[i],step+1));

                                  visit.insert(neighbors[i]);

                              }

                          }

                      }

                      return 0;

                  }

                    

最终代码

class Solution {

public:

    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {

        std::map<std::string,std::vector<std::string>> graph;

        construct_graph(beginWord,wordList,graph);

        return BFS_graph(beginWord,endWord,graph);

    }

private:

bool connect(const std::string &word1,const std::string &word2)

{

int cnt =0;

for(int i =0 ;i<word1.length();i++)

{

   if(word1[i]!=word2[i])

   {

       cnt++;

   }

}

return cnt ==1;

}

 

void construct_graph(std::string &beginWord,

                     std::vector<std::string>& wordList,

                     std::map<std::string,std::vector<std::string>> &graph)

                     {

                         wordList.push_back(beginWord);

                         for(int i = 0;i<wordList.size();i++)

                         {

                             graph[wordList[i]]=std::vector<std::string> ();

                         }

                         for(int i = 0;i<wordList.size();i++)

                         {

                             for(int j = i+1;j<wordList.size();j++)

                             {

                                 if(connect(wordList[i],wordList[j]))

                                 {

                                     graph[wordList[i]].push_back(wordList[j]);

                                     graph[wordList[j]].push_back(wordList[i]);

                                 }

                             }

                         }

                     }

                    

                    

    int BFS_graph(std::string &beginWord,std::string &endWord,

                  std::map<std::string,std::vector<std::string>> &graph)

                  {

                      std::queue<std::pair<std::string,int>> Q;

                      std::set<std::string> visit;

                      Q.push(std::make_pair(beginWord,1));

                      visit.insert(beginWord);

                      while(!Q.empty())

                      {

                          std::string node = Q.front().first;

                          int step = Q.front().second;

                          Q.pop();

                          if(node==endWord)

                          {

                              return step;

                          }

                          const std::vector<std::string> &neighbors = graph[node];

                          for(int i = 0;i<neighbors.size();i++)

                          {

                              if(visit.find(neighbors[i])==visit.end())

                              {

                                  Q.push(std::make_pair(neighbors[i],step+1));

                                  visit.insert(neighbors[i]);

                              }

                          }

                      }

                      return 0;

                  }

};

126 单词接龙2

给定两个单词(beginWord endWord)和一个字典 wordList,找出所有从 beginWord endWord 的最短转换序列。转换需遵循如下规则:

每次转换只能改变一个字母。

转换过程中的中间单词必须是字典中的单词。

说明:

如果不存在这样的转换序列,返回一个空列表。

所有单词具有相同的长度。

所有单词只由小写字母组成。

字典中不存在重复的单词。

你可以假设 beginWord endWord 是非空的,且二者不相同。

示例 1:

输入:

beginWord = "hit",

endWord = "cog",

wordList = ["hot","dot","dog","lot","log","cog"]

输出:

[

["hit","hot","dot","dog","cog"],

["hit","hot","lot","log","cog"]

]

示例 2:

输入:

beginWord = "hit"

endWord = "cog"

wordList = ["hot","dot","dog","lot","log"]

输出: []

解释: endWord "cog" 不在字典中,所以不存在符合要求的转换序列。

来源:力扣(LeetCode

链接:力扣

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思考:

 

在建立beginWordwordList的连接时,若单词表已包含beginWord,按照上题方法,建立图会出现什么问题

在宽度优先搜索时,如何保存宽度搜索时的路径?

1.将普通队列更换为vector实现队列,保存所有的搜索节点,即在pop节点时不会丢弃队头元素,只是移动front指针。

2.在队列节点中增加该节点的前驱节点在队列中的下标信息,可通过该下标找到的是队列中的哪个节点搜索到的当前节点

struct Qitem

{

    std::string node;

    int parent_pos;

    int step;

    Qitem(std::string node,int parent_pos,int step):node(node),parent_pos(parent_pos),step(step) {}

};

若起始点与终点有多条路径,如何将多条路径全部搜索出?

到达某一位置可能存在多条路径,使用映射记录到达每个位置的最短需要的步数,新拓展到的位置只要未曾到达或到达步数与最短步数相同,即将该位置添加至队列中,从而存储了从不同前驱到达该位置的情况

void BFS_graph(std::string &beginWord,std::string &endWord,

               std::map<std::string,std::vector<std::string>> &graph,

               std::vector<Qitem> &Q,//使用vector实现的队列,可保存所有信息

               std::vector<int> &end_word_pos)//终点endWord所在队列的位置下标

               {

                   std::map<std::string,int> visit; //单词,步数

                   int min_step = 0;

                   Q.push_back(Qitem(beginWord.c_str(),-1,1));

                   visit[beginWord] = 1;

                   int front = 0;

                   while(front != Q.size())

                   {

                       const std::string &node = Q[front].node;

                       int step = Q[front].step;

                       if(min_step !=0 && step> min_step)

                       {

                           break;

                       }

                       if(node ==endWord)

                       {

                           min_step = step;

                           end_word_pos.push_back(front);

                       }

                       const std::vector<std::string> &neighbors = graph[node];

                       for(int i = 0;i<neighbors.size();i++)

                       {

                           if(visit.find(neighbors[i]) == visit.end() || visit[neighbors[i]]==step+1 )

                           {

                              

                               Q.push_back(Qitem(neighbors[i],front,step+1));

                               visit[neighbors[i]] = step +1;

                           }

                       }

                       front ++;

                   }

                   

               }

图的建立问题修改

  bool connect(const std::string &word1,const std::string &word2)

{

int cnt =0;

for(int i =0 ;i<word1.length();i++)

{

   if(word1[i]!=word2[i])

   {

       cnt++;

   }

}

return cnt ==1;

}

 

 

 void construct_graph(std::string &beginWord,

                         std::vector<std::string>& wordList,

                         std::map<std::string,std::vector<std::string>> &graph)

                         {

                             int has_begin_word = 0;

                             for(int i = 0; i<wordList.size();i++)

                             {

                                 if(wordList[i] == beginWord)

                                 {

                                     has_begin_word = 1;

                                 }

                                 graph[wordList[i]] = std::vector<std::string>();

                                

                             }

                             for(int i = 0;i<wordList.size();i++)

                             {

                                 for(int j = i+1;j<wordList.size();j++)

                                 {

                                     if(connect(wordList[i],wordList[j]))

                                     {

                                         graph[wordList[i]].push_back(wordList[j]);

                                         graph[wordList[j]].push_back(wordList[i]);

                                     }

                                 }

                                 if(has_begin_word == 0 && connect(beginWord,wordList[i]))

                                 {

                                     graph[beginWord].push_back(wordList[i]);

                                 }

                             }

                         }

遍历搜索路径:

class Solution {

public:

    vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {

        std::map<std::string,std::vector<std::string>> graph;

        construct_graph(beginWord,wordList,graph);

        std::vector<Qitem> Q;

        std::vector<int> end_word_pos;

        BFS_graph(beginWord,endWord,graph,Q,end_word_pos);

        std::vector<std::vector<std::string>> result;

        for(int i = 0;i<end_word_pos.size();i++)

        {

            int pos = end_word_pos[i];

            std::vector<std::string> path;

            while(pos!=-1)

            {

                path.push_back(Q[pos].node);

                pos = Q[pos].parent_pos;

            }

            result.push_back(std::vector<std::string>());

            for(int j = path.size()-1;j>=0;j--)

            {

                result[i].push_back(path[j]);

            }

        }

        return result;

 

    }

};

最终代码

struct Qitem

{

    std::string node;

    int parent_pos;

    int step;

    Qitem(std::string node,int parent_pos,int step):node(node),parent_pos(parent_pos),step(step) {}

};

bool connect(const std::string &word1,const std::string &word2)

{

int cnt =0;

for(int i =0 ;i<word1.length();i++)

{

   if(word1[i]!=word2[i])

   {

       cnt++;

   }

}

return cnt ==1;

}

 

void BFS_graph(std::string &beginWord,std::string &endWord,

               std::map<std::string,std::vector<std::string>> &graph,

               std::vector<Qitem> &Q,//使用vector实现的队列,可保存所有信息

               std::vector<int> &end_word_pos)//终点endWord所在队列的位置下标

               {

                   std::map<std::string,int> visit; //单词,步数

                   int min_step = 0;

                   Q.push_back(Qitem(beginWord.c_str(),-1,1));

                   visit[beginWord] = 1;

                   int front = 0;

                   while(front != Q.size())

                   {

                       const std::string &node = Q[front].node;

                       int step = Q[front].step;

                       if(min_step !=0 && step> min_step)

                       {

                           break;

                       }

                       if(node ==endWord)

                       {

                           min_step = step;

                           end_word_pos.push_back(front);

                       }

                       const std::vector<std::string> &neighbors = graph[node];

                       for(int i = 0;i<neighbors.size();i++)

                       {

                           if(visit.find(neighbors[i]) == visit.end() || visit[neighbors[i]]==step+1 )

                           {

                              

                               Q.push_back(Qitem(neighbors[i],front,step+1));

                               visit[neighbors[i]] = step +1;

                           }

                       }

                       front ++;

                   }

                  

               }

              

    void construct_graph(std::string &beginWord,

                         std::vector<std::string>& wordList,

                         std::map<std::string,std::vector<std::string>> &graph)

                         {

                             int has_begin_word = 0;

                             for(int i = 0; i<wordList.size();i++)

                             {

                                 if(wordList[i] == beginWord)

                                 {

                                     has_begin_word = 1;

                                 }

                                 graph[wordList[i]] = std::vector<std::string>();

                                

                             }

                             for(int i = 0;i<wordList.size();i++)

                             {

                                 for(int j = i+1;j<wordList.size();j++)

                                 {

                                     if(connect(wordList[i],wordList[j]))

                                     {

                                         graph[wordList[i]].push_back(wordList[j]);

                                         graph[wordList[j]].push_back(wordList[i]);

                                     }

                                 }

                                 if(has_begin_word == 0 && connect(beginWord,wordList[i]))

                                 {

                                     graph[beginWord].push_back(wordList[i]);

                                 }

                             }

                         }

class Solution {

public:

    vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {

        std::map<std::string,std::vector<std::string>> graph;

        construct_graph(beginWord,wordList,graph);

        std::vector<Qitem> Q;

        std::vector<int> end_word_pos;

        BFS_graph(beginWord,endWord,graph,Q,end_word_pos);

        std::vector<std::vector<std::string>> result;

        for(int i = 0;i<end_word_pos.size();i++)

        {

            int pos = end_word_pos[i];

            std::vector<std::string> path;

            while(pos!=-1)

            {

                path.push_back(Q[pos].node);

                pos = Q[pos].parent_pos;

            }

            result.push_back(std::vector<std::string>());

            for(int j = path.size()-1;j>=0;j--)

            {

                result[i].push_back(path[j]);

            }

        }

        return result;

 

    }

};

473火柴棍摆正方形

还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。

输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。

示例 1:

输入: [1,1,2,2,2]

输出: true

解释: 能拼成一个边长为2的正方形,每边两根火柴。

示例 2:

输入: [3,3,3,3,4]

输出: false

解释: 不能用所有火柴拼成一个正方形。

注意:

给定的火柴长度和在 0 10^9之间。

火柴数组的长度不超过15

来源:力扣(LeetCode

链接:力扣

回溯法:

想象正方形4条边为4个通,将每个火柴回溯的位置在每个桶中,在放完n个火柴杆后,检查4个桶中的火柴杆长度和是否相同,相同则返回真,否则返回假;在回溯过程中,如果当前所有可能向后的回溯,都无法满足条件,即递归函数最终返回假。

优化1n个火柴总和对4取余需要为0,否则返回假。

优化2:火柴杆按照从小到大的顺序排序,先尝试大的减少回溯可能。

优化3:每次放置时,每条边上不可放置超过总和的1/4长度的火柴杆

class Solution {

public:

    bool makesquare(vector<int>& nums) {

        if(nums.size()<4)

        {

            return false;

        }

        int sum = 0;

        for(int i = 0;i<nums.size();i++)

        {

            sum +=nums[i];

        }

        if(sum % 4)

        {

            return false;

        }

        std::sort (nums.rbegin(),nums.rend());

        int bucket[4] = {0};

        return generate(0,nums,sum/4,bucket);

    }

    private:

    bool generate(int i,std::vector<int>&nums,int target,int bucket[])

    {

        if(i>=nums.size())

        {

            return bucket[0] == target && bucket[1] == target

            && bucket[2] == target && bucket[3] == target;

        }

        for(int j = 0;j<4;j++)

        {

            if(bucket[j]+nums[i]>target)

            {

                continue;

            }

            bucket[j]+=nums[i];

            if(generate(i+1,nums,target,bucket))

            {

                return true;

            }

           bucket[j]-=nums[i];

        }

        return false;

    }

};

位运算法:

使用位运算法,构造出所有和为target(总和/4)的子集,存储在向量ok_subset中,这些是候选的边组合。

2.遍历所有的ok_subset,两两进行对比,如果ok_set[i]ok_set[j]进行与运算的结果为0,则说明ok_set[i]ok_set[j]表示的是无交集的两个集合(没有选择同样的火柴棍),这两个集合可以代表两个同时存在的满足条件的边,将ok_set[i]ok_set[j]求或,结果存储在ok_half中,它代表所有满足一半结果的情况。

3.遍历所有的ok_half,两两进行对比,如果ok_half[i]ok_half[j]进行与运算的结果的结果为0,则返回true(说明4个满足条件的边,即可组成正方形);否则返回false.

class Solution {

public:

    bool makesquare(vector<int>& nums) {

        if(nums.size()<4)

        {

            return false;

        }

        int sum = 0;

        for(int i = 0;i<nums.size();i++)

        {

            sum +=nums[i];

        }

        if(sum % 4)

        {

            return false;

        }

        int target = sum/4;

        std::vector<int> ok_subset;

        std::vector<int> ok_half;

        int all = 1<<nums.size(); //2^nums.size()

        for(int i = 0;i<all;i++)

        {

            int sum = 0;

            for(int j=0;j<nums.size();j++)

            {

                if(i&(1<<j))

                {

                    sum += nums[j];

                }

            }

            if(sum == target)

            {

                ok_subset.push_back(i);

            }

        }

        for(int i = 0;i<ok_subset.size();i++)

        {

            for(int j = i+1;j<ok_subset.size();j++)

            {

                if((ok_subset[i] & ok_subset[j])==0)

                {

                    ok_half.push_back(ok_subset[i] | ok_subset[j]);

                }

            }

        }

        for(int i = 0;i<ok_half.size();i++)

        {

            for(int j = i+1;j<ok_half.size();j++)

            {

                if((ok_half[i] & ok_half[j])==0)

                {

                    return true;

                }

            }

        }

        return false;

    }

  

};

407 收集雨水(待学习)

给你一个 m x n 的矩阵,其中的值均为非负整数,代表二维高度图每个单元的高度,请计算图中形状最多能接多少体积的雨水。

示例:

给出如下 3x6 的高度图:

[

[1,4,3,1,3,2],

[3,2,1,3,2,4],

[2,3,3,2,3,1]

]

返回 4

https://picb.zhimg.com/80/v2-4c60ca34b4f167673f992c7e63d020b1_1440w.jpg

如上图所示,这是下雨前的高度图[[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]] 的状态。

https://pic3.zhimg.com/80/v2-ff2455d6f43805b8a770b029f57611c2_1440w.jpg

下雨后,雨水将会被存储在这些方块中。总的接雨水量是4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值