挑战程序设计竞赛笔记-准备篇和穷举

第一章 准备篇

1.1 OJ结果:

Accepted :通过用例
Time Limit Exceed :超时
Runtime Error: 程序非法访问内存或未处理异常
Memory Limit Exceeded: 内存超限
Presentation Error: 输出的内容对的,但是格式不符合
Output Limit Exceeded : 输出过多内容
Compile Error 未能编译

1.2 运行时间
对于一个限制时间为1秒的题目,我们要估计算法的复杂度。并将数值可能最大的值带入其中

如:n²的复杂度,若n≤1000,则平方为 1000 000 .
	在1秒的限制时间内完成:
	1000000  游刃有余
	10000000 勉勉强强
	100000000 几乎不可能

第二章 初级篇

2.1 穷举搜索

2.1.1 栈
#include<stack> 可以直接调用c++自带的栈库

stack<int> s 使用模板类来进行声明。
s.push(3) 向栈中推进int 3.
s.pop()是推出。但是并不返回值
s.top()才是返回值。
2.1.2 队列
#include<queue>

queue<int> 可以声明

需要注意的是,queue.front()才是返回队首的值,而pop也是将其推出。
2.1.3 深度优先搜索DFS

下面这道题是一个判断农田水洼数目的题。

由于最近的降雨,水汇集在Farmer John's田地的不同地方,其由N×M(1 <= N <= 100; 1 <= M <= 100)的正方形矩形表示。每个方块包含水('W')或旱地('。')。农夫约翰想弄清楚他的田地里有多少个池塘。池塘是一组连接的正方形,其中有水,其中一个正方形被认为与其所有八个邻居相邻。 

10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.
  • [√] 这道题用了深度搜索。也就是,从一个点开始判断,判断其是否是水洼,如果是水洼,则判断其周围的8个点是不是水洼。只要有一个是水洼,就再以这个点为基准点,进行递归再次搜索其周围的8个点。

最终结果是对每个点至多会有8次循环,对于搜索到的点,都会对其周围八个点进行搜索,进行8次循环。因此,复杂度小于O(8NM)。
并且,搜索过的水洼点会被置为’.’。在外部循环就不会被再次遍历。

思路就是,对整个农田进行遍历,遇到一个水洼就进行深度搜索,直到将其相邻水洼都置为’.’。外部循环的每次返回相当于一个池塘被填平。我们只要记录池塘被填平的次数就可以了。

#include<stdio.h>

int N,M;
int Max_N=100;
int Max_M=100;
char field[100][100];//田地的大小

void DFS(int x,int y){
    field[x][y]='.';
    for(int x_=-1;x_<=1;x_++){
        for(int y_=-1;y_<=1;y_++){//每个点的上下左右
            
            int ix = x+x_;
            int iy = y+y_;
            if(ix<N&&iy<M&&field[ix][iy]=='W'&&ix>=0&&iy>=0)DFS(ix,iy);//只有水洼点才有继续遍历的必要
        }
    }
    return;
}

int main(){
    while(scanf("%d %d",&N,&M)!=EOF){
    for(int i=0;i<N;i++){
        getchar();
        for(int j=0;j<M;j++){
            scanf("%c",&field[i][j]);
        }
    }
    //获取数据
    int count=0;
    for(int i=0;i<N;i++){
        for(int j=0;j<M;j++){
            if(field[i][j]=='W'){//只有遇到水洼点,才进行深度遍历
                DFS(i,j);
                count++;
            }
        }
    }
    printf("%d\n",count);
    }
    return 0;
}
2.1.4 广度优先遍历

广度优先搜索就是优先查找和当前状态相似的状态,比如迷宫问题,寻找通向终点的最短路径。

从起点开始,记录到四个方向的距离(第一步是1),然后将这四个方向的坐标放入队列当中。第二步,从队列中取出一个方向的坐标,表示从这里开始,再找他的四个方向。并且这次记录的距离是从起点到它这里的距离(1)再加上1.将距离和坐标记录后,将新的四个方向放入队列。接下来,再从队列取出一个坐标,这是第一步的第二个方向。当队列为空时,说明没有可达点了。这时再看起点到终点的距离是多少。如果是初始化值如inf,则说明终点不可达。

与深度优先不同,深度优先是一直进行下去,直到“触底”后,再回到上一步的另一个分支。而广度优先则是在起点先把从起点的分支遍历一次,再遍历二级分支一次,直到没有分支可以遍历。

2.1.5 枚举

使用库中的next_permutation(start,start+n)可以将数组start生成n!个枚举。

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int perm2[10];

void permutation(int n){
    for(int i=0;i<n;i++){
        perm2[i]=i;
    }
    while(std::next_permutation(perm2,perm2+n)){//当next_permutation函数生成了所有枚举后,就会返回false。
        for(int i=0;i<n;i++){
            printf("%d ",perm2[i]);
        }
        printf("\n");
    }
}
int main(){
    
    //printf("11");
    permutation(4);
}

但是要注意,似乎生成的n!不包括其原本顺序。

另外,也可以用一个类似深度优先遍历的方法来生成。

	bool used[100] 
	int perm[100]
	//这里生成 0--100的所有序列。

  perm1(pos,n){
	if(pos==n){//说明100个数字已经填满一个队列
		打印队列
		return;
	}
	for(int i=0;i<n;i++){
	//这里n=100
		if(used[i]==false){
		  perm[pos]=i;//在第pos个位置放数字i
		  used[i]=true;
		  perm1(pos+1,n);
		  used[i]=false;
		}	
	}
		  return;//当这一个pos的可用数字全部用完了,就让其上一个Pos的值改变。再进行深度操作。

	}
2.1.6 剪枝

上面所说的穷举法,可能会因为解空间过大而时间复杂度过高。在判断过程中,可能会有一些细节能让我们知道该路径可以提前结束。从而减少不必要的搜索活动。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值