算法复习之枚举(并不是非常简单)

基于已有知识进行答案猜测:没有公式可以直接计算,只能尝试
例如:判断N是否是复数——转化为求小于N的所有复数

枚举的思想 :猜测
从可能的集合中一一列举各元素,对问题可能解集合的每一项进行检验,判定条件是否成立

注意:
  1. 猜测的结果必须是没有出现过的
  2. 要及早排除错误答案

三个关键问题:

  1. 给出解空间,建立简洁的数学模型——模型中的变量尽可能少,之间相互独立
  2. 减少搜索的空间,利用知识缩小变量取值范围,避免不必要的计算——减少代码中循环体的执行次数
  3. 采用合适的搜索顺序——搜索空间的遍历顺序要与模型中的条件表达式一致,例如应该从小到大之类的

注意多重循环的时候,不同循环变量之间的相对大小关系,取决于升序降序,优先比较结果的哪个方面之类的
巧妙代码示例:(求生理周期)

int p, e, i, d, caseNo = 0;
while( cin >> p >> e >>i >>d && p!= -1 )
 {
	++ caseNo;
	int k;
	for(k = d+1; (k-p)%23; ++k); // 凡是成功则跳出
	for(; (k-e)%28; k+= 23);    // 保证上一个的前提下,使之成功第二个
	for(; (k-i)%33; k+= 23*28);  // 亦然
	cout << "Case " << caseNo << ": the next triple peak occurs in "<< k-d <<" days."<< endl;
}

例题总结:

  1. 判断试验顺序是否对最终结果有影响,如果没有,自己规划恰当的顺序,比如总可能性小的先开始,从第一行先开始
  2. 判断前面的尝试是否对后面的判断有影响,是否已经自动减少了可能,要利用之间的相互关系,减少搜索空间——如果存在某个局部, 一旦这个局部的状态被确定,剩余其他部分的状态只能是确定的一种, 或不多的n种——[问题的关键]: 只需枚举这个局部的状态即可
  3. 实验寻求操作满足的题目,可以,也可以不,去保留暂态,方便的话可以临时算来确定
  4. 可能性多少固定的问题,可以化为数的进制来枚举,代码比较简洁,比如两种可能可以记为二进制,然后用++进位来枚举
  5. 枚举顺序十分重要, 好的枚举顺序, 能够及早排除不可能的情况, 减少枚举次数

例题:讨厌的青蛙(寻找经历最多节点的数量)

#include 
#include 
#include 
using namespace std;
int r, c, n; // 行列,水稻数
struct PLANT
{
    int x, y;
};
PLANT plants[5001]; // 踩踏的水稻
int searchPath(PLANT secPlant, int dX, int dY); 
int main()
{
    int i, j, dX, dY, pX, pY, steps, max = 2; // 步长, 起点, 步数, 最大值
    scanf("%d %d", &r, &c);
    scanf("%d", &n);
    for (i = 0; i < n; i++)
        scanf("%d %d", &plants[i].x, &plants[i].y);
    sort(plants, plants + n); // sort函数调用自己重载的 < , 优先排列x, 其次排列y
    for (i = 0; i < n - 2; i++)
    {
        for (j = i + 1; j < n - 1; j++) // 假设从上方跳入(直线无方向),故第二步总是在第一步后面
        {
            dX = plants[j].x - plants[i].x;
            dY = plants[j].y - plants[i].y;
            pX = plants[i].x - dX;
            pY = plants[i].y - dY;
            if (pX <= r && pX >= 1 && pY <= c && pY >= 1) // 第一步名不副实,继续
                continue;
            if (plants[i].x + (max - 1) * dX > r) // 对于x步数注定较少,之后更少,跳出
                break;
            pY = plants[i].y + (max - 1) * dY; // 对于y, 步数较少,还可挽救,故继续
            if (pY > c || pY < 1)
                continue;
            steps = searchPath(plants[j], dX, dY); // 
            if (steps > max)
                max = steps;
        }
    }
    if (max == 2)
        max = 0;
    printf("%d\n", max);
    return 0;
}
bool operator<(const PLANT &p1, const PLANT &p2)
{
    if (p1.x == p2.x)
        return p1.y < p2.y;
    return p1.x < p2.x;
}
int searchPath(PLANT secPlant, int dX, int dY) // 从第二步计算步数
{
    PLANT plant;
    int steps;
    plant.x = secPlant.x + dX; // 下一步
    plant.y = secPlant.y + dY;
    steps = 2;
    while (plant.x <= r && plant.y <= c && plant.x >= 1 && plant.y >= 1) // 下一步仍在
    {
        if (!binary_search(plants, plants + n, plant))// 下一步没有踩中
        {
            steps = 0;
            break;
        }
        plant.x += dX; // 进步
        plant.y += dY;
        steps++;
    }
    return steps;
}

个人觉得在纷繁复杂的题目中理出头绪,找出线索比较重要

你可能感兴趣的:(freshman)