PAT甲级备考——DFS+剪枝

PAT甲级备考——DFS+剪枝

  • 题目
    • 【1103】DFS + 剪枝
    • 【1131】dfs深度优先搜索

题目

PAT (Advanced Level) Practice
深度优先搜索DFS:
1013、1021、1034、1103、1130、1131、1134
广度优先搜索BFS:
1076、1091

【1103】DFS + 剪枝
【1131】dfs深度优先搜索

【1103】DFS + 剪枝

题⽬⼤意:给三个正整数N、K、P,将N表示成K个正整数(可以相同,递减排列)的P次⽅和,如果有多种⽅案,选择底数n1+…+nk最⼤的⽅案,如果还有多种⽅案,选择底数序列的字典序最⼤的⽅案

#include
#include
#include
#include
using namespace std;


int n, p, k, maxFacSum = -1;
vector<int> v, tempAns, ans;


// 初始化数组v,其中保存着v[i] = i^k;
void init(){
    int temp = 0, index = 1;
    while(temp <= n){
        v.push_back(temp);
        temp = pow(index,p);
        index++;
    }
}

// 深度优先遍历
void dfs(int index, int tempK, int tempSum, int factorSum){
    // 从后往前深度遍历v;
    // index为当前遍历的节点,tempK为当前已经相加的节点的数目,tempSum为当前相加节点之和,factorSum为当前相加节点因子和
    if(tempK == k){ // 如果当前相加节点数目达到目标值k
        if(tempSum==n && maxFacSum<factorSum){ // 如果当前相加节点之和为目标值n,且factorSum大于最大的因子和
            maxFacSum = factorSum; // 更新最大因子和
            ans = tempAns; // 将当前答案数组tempAns储存至结果数组ans
        }
        return; // 返回
    }
    while(index>=1){ // 如果当前tempK不等于K,从后向前遍历v数组,直至全部遍历完
        if(tempSum + v[index] <= n){ // 如果当前加和tempSum加数组v中index对应的值小于等于n
            tempAns[tempK] = index; // 记录当前第tempK加和的数为index
            dfs(index, tempK+1, tempSum+v[index], factorSum+index); // 深度遍历,其中包含回溯思想
        }
        if(index==1) return; 如果index已经减小到1,则返回
        index--; // index 向前移动
    }
}


int main(){
    cin>>n>>k>>p; // n:加和;k:节点的数目;p:因子的p次方
    init(); // 初始化数组v,其中保存着v[i] = i^k;
    tempAns.resize(k);
    dfs(v.size()-1, 0, 0, 0); // 深度遍历,从v的最后一个元素开始
    if(maxFacSum == -1){//并没有更新这个最大因子和
        cout<<"Impossible\n";
    }else{
        printf("%d = ",n);
        for(int i=0; i<k; i++){
            printf("%d^%d",ans[i], p);
            if(i!=k-1) printf(" + ");
        }
    }
}

剪枝策略,属于算法优化范畴;通常应用在DFS 和 BFS 搜索算法中;
剪枝策略就是寻找过滤条件,提前减少不必要的搜索路径。

剪枝优化三原则:
(1)正确:不把最优解的部分剪掉
(2)准确:合适的判断手段,准确性是衡量优化算法好坏的标准
(3)高效:减少搜索的次数, 在优化和效率之间找到平衡点

分类:
(1)可行性剪枝:判断继续搜索能否得出答案,如果不能则回溯
(2)最优性剪枝:上下界剪枝,记录当前得到最优值,如果当前节点已无法产生比当前最优解更优的解法,可以提前回溯

实例分析:
问题:N个城市,编号1到N。城市间有R条单向道路。每条道路连接两个城市,有长度和过路费两个属性。Bob只有K块钱,他想从城市1走到城市N。问最短共需要走多长的路。如果到不了N,输出-1。2<=N<=100;0 每条路的长度 L, 1 <= L <= 100
每条路的过路费T , 0 <= T <= 100

思路:从城市 1开始深度优先遍历整个图,找到所有能过到达 N 的走法,选一个最优的。
如何剪枝?
1.最优化剪枝:如果当前已经找到的最优路径长度为L ,那么在继续搜索的过程中,总长度已经大于等于L的走法,就可以直接放弃,不用走到底了
2.可行性剪枝:如果当前到达城市的路费已大于k,或者等于k且没有到达终点,就可以直接放弃。

————————————————
参考链接:https://blog.csdn.net/u010700335/article/details/44079069
原文链接:https://blog.csdn.net/hzaukotete/article/details/81173143

【1131】dfs深度优先搜索

1131 Subway Map (30 分)
题⽬⼤意:找出⼀条路线,使得对任何给定的起点和终点,可以找出中途经停站最少的路线;如果经停站⼀样多,则取需要换乘线路次数最少的路线

注意:
输入的index为int类型时,输出时要考虑填充0的情况,保存成1,输出为0001;

(一)输入,构建v和line
输入的地铁站和路线信息主要保存在v和line中;
v:存储着每个节点它所相邻的节点列表,如v[3212] = {3066,3008,1003,1001}
line:第一个int是一对节点,前四位为节点1的index,后四位为节点2的index;
第二个int是两个节点共同所在的地铁线名称,如:32123008:3

(二)搜索最短路径
(1)输入起点和终点
(2)dfs深度搜索,生成最短路径path
如果当前符合一定条件更新最小路径;否则,遍历当前节点的邻接节点,深度遍历
(3)输出最短路径所经过的节点数,遍历path,输出每次经过的换乘节点
判断是否时换乘点,要遍历相邻的三个节点A B C,若A和B所在线路与B和C所在线路不同,则换乘节点为B

#include
#include
#include
using namespace std;
vector<vector<int>> v(10000); // 存储着每个节点它所相邻的节点列表,如v[3212] = {3066,3008,1003,1001}
vector<int> path, temp_path;
int min_node, min_trans, visited[10000];
unordered_map<int, int> line; // 第一个int是一对节点,前四位为第一个节点的index,后四位为第二个节点的index;第二个int是两个节点共同所在的地铁线名称,如:32123008:3
// 原本可以设置line[10000][10000]的数组,去保存两个节点所在的线路,但是数组空间溢出,这里选择用map代替
int line_n, search_n, start, destination;//line_n 地铁线路的数目,search_n查询的数目,查询起始点start,查询终止点destination
int cnt; // dfs中保存当前遍历了多少个节点

int transfer_node(vector<int> temp){
    // 计算当前路径temp下换乘的节点数,即依次遍历相邻节点
    // 如:遍历到 A B C,若A和B所在线路与B和C所在线路不同,则换乘节点为B
    int trans_cnt = -1, pre_line = 0, temp_size = temp.size();
    for(int i=1; i<temp_size; i++){
        if(line[temp[i-1]*10000+temp[i]] != pre_line) trans_cnt++;
        pre_line = line[temp[i-1]*10000+temp[i]];
    }
    return trans_cnt;
}

void dfs(int node, int cnt){
    /*
        如果当前符合一定条件更新最小路径;
        否则,遍历当前节点的邻接节点,深度遍历
    */
    if(node == destination && (cnt < min_node || (cnt == min_node && transfer_node(temp_path) < min_trans))){
        // 如果当前的起始点等于重点,且当前的节点数小于最小的节点数,或者当前的节点数等于最小节点数但当前路径换乘的节点数小于最小换乘节点数
        // 更新最小节点数,最小换乘节点数,最终的路径
        min_node = cnt;
        min_trans = transfer_node(temp_path);
        path = temp_path;
    }
    if(node == destination) return;
    for(int i=0; i<v[node].size(); i++){
        // 如果当前未走到终点,则深度迭代遍历当前起点的所有邻居
        if(visited[v[node][i]]==0){ // 如果邻居i未被访问,则访问i
            visited[v[node][i]] = 1;
            temp_path.push_back(v[node][i]); // 当前路径中加入i
            dfs(v[node][i], cnt+1); // 深度访问i,记录当前节点数cnt++
            temp_path.pop_back(); // 回溯,当前路径pop
            visited[v[node][i]] = 0; // 回溯未访问i;注意这是图,所以要更新为未访问
        }
    }
}
int main(){
    
    // (1)输入,构建v和line
    cin>>line_n;
    int k, pre, cur;
    for(int i=1; i<=line_n; i++){
        scanf("%d %d",&k, &pre); // 输入总的节点数、第一个节点的值
        for(int j=1; j<k; j++){
            scanf("%d", &cur); // 接着输入其他节点cur
            v[pre].push_back(cur); //设置pre和cur的接邻节点为互相
            v[cur].push_back(pre);
            line[pre*10000+cur] = line[cur*10000+pre] = i; //且pre和cur在同一条线路i中
            pre = cur;
        }
    }
    scanf("%d", &search_n);
    
    // (2)搜索最短路径
    while(search_n--){
        // 输入起点和终点
        min_node = 100000, min_trans = 100000;
        scanf("%d %d", &start, &destination);
        
        // dfs深度搜索,生成最短路径path
        temp_path.clear(); // temp_path更新
        temp_path.push_back(start);
        visited[start] = 1;
        dfs(start, 0);
        visited[start] = 0;
        
        //  输出最短路径所经过的节点数,遍历path,输出每次经过的换乘节点
        cout<<min_node<<endl;
        int pre_line = 0, trans_node = start;
        
        for(int i=1; i<path.size(); i++){
            if(line[path[i-1]*10000+path[i]] != pre_line){
                if (pre_line != 0) printf("Take Line#%d from %04d to %04d.\n", pre_line, trans_node, path[i-1]);
                trans_node = path[i-1];
                pre_line = line[path[i-1]*10000+path[i]];
            }
        }
        printf("Take Line#%d from %04d to %04d.\n", pre_line, trans_node, destination);   
    }
    return 0;
}

你可能感兴趣的:(PAT,深度优先,剪枝,算法)