• P3959 宝藏


    P3959 宝藏

    题目描述
    参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 nn 个深埋在地下的宝藏屋, 也给出了这 nn 个宝藏屋之间可供开发的 mm 条道路和它们的长度。

    小明决心亲自前往挖掘所有宝藏屋中的宝藏。但是,每个宝藏屋距离地面都很远, 也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路 则相对容易很多。

    小明的决心感动了考古挖掘的赞助商,赞助商决定免费赞助他打通一条从地面到某 个宝藏屋的通道,通往哪个宝藏屋则由小明来决定。

    在此基础上,小明还需要考虑如何开凿宝藏屋之间的道路。已经开凿出的道路可以 任意通行不消耗代价。每开凿出一条新道路,小明就会与考古队一起挖掘出由该条道路 所能到达的宝藏屋的宝藏。另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏 屋之间的道路无需再开发。

    新开发一条道路的代价是:

    [mathrm{L} imes mathrm{K} ]

    L代表这条道路的长度,K代表从赞助商帮你打通的宝藏屋到这条道路起点的宝藏屋所经过的 宝藏屋的数量(包括赞助商帮你打通的宝藏屋和这条道路起点的宝藏屋) 。

    请你编写程序为小明选定由赞助商打通的宝藏屋和之后开凿的道路,使得工程总代 价最小,并输出这个最小值。


    错误日志: 错太多次了。。写总结写下面好了


    Solution

    经由很多人讨论得出, 此题靠谱正解为搜索
    搜索啊, 毒瘤啊

    那么现在谈谈这题怎么剪枝
    首先最基本的最优化剪枝, 包含两点:

    最优化剪枝

    1. 当搜索到某处发现此时记录的答案已经没有记录的最优答案优, 结束此次搜索

    2. 设计一个估价, 为当前状态下, 至少需要多少代价才能到达目标状态, 当估价值加上现值大于最优记录答案, 结束此次搜索

    啥意思呢? 就是你要设计一个东西, 记为 (g(x)), 且 (g(x) leq) 现实值
    当如今 (tot + g(x) geq ans) 说明 现实值$ + tot < ans$ , 此时在搜索下去已经没有意义了, 需要返回
    设计的 (g(x)) 需要容易计算, 其值越接近现实值剪枝效率越高

    因为此题中说明了小明建出来的东西是一颗树, 所以每个点只会仅仅被一个点更新
    故我们记录到这个点 (x) 的最小距离, 记为 (MIN_{x}), 当前已决策集合为 (S)
    那么估价函数就设为 $$sum_{i otin S}MIN_{i}$$
    其一定小于现实值没得洗
    每向决策中加入一个点, 就从估价里拿出这部分, 当把点去除时加回去即可

    优化搜索顺序

    (dfs) 看做建一栋楼
    那么搜索的过程就是不断建楼, 拆楼的过程
    建到最后发现建得不和自己的心意(边界), 亦或是建到一半发现太丑了最后肯定看不得(剪枝)
    你会把楼拆了继续坚持建下去, 直到尝试完所有可行的建法为止(搜索结束)
    不断尝试 更新 进步 超越
    这就是固执的 (dfs)
    人生亦然啊

    咳咳扯得有点远
    说真的老拆楼建楼挺累的
    能不能设计一个蓝图
    让我们能尽可能少拆楼呢?
    要拆也是尽量少拆, 然后在这个基础上继续建
    这就是优化搜索顺序

    感性理解了以后, 我们在代码基础上认识一下如何在代码中实现优化搜索顺序
    简单来说, 我们需要确定一种搜索顺序, 使得重复搜索的规模尽可能小

    此题中, 我们先确定搜索框架: 枚举已选中的点, 对于这些点选定还未加入集合的点, 加入并更新答案, 进一步加深搜索
    那么怎么确定搜索顺序使其最优呢?
    通过观察, 我们可以发现, 当确定选中的点后, 我们选未加入集合的点, 对于同一个点, 搜索完后只有两个可能:

    1. 此点被选中, 此时选中集合改变
    2. 此点没被选中, 那么这个点(在这个集合状态下)不可能在被选了

    综上, 我们设立两个起点, (begin1, begin2), 前一个记录选中集合内点枚举到了哪个, 后一个记录在当前集合下枚举没入集合的点到了哪个
    新一轮搜索时, 从断点开始, 大大减少搜索量

    Code

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define LL long long
    #define REP(i, x, y) for(int i = (x);i <= (y);i++)
    using namespace std;
    int RD(){
        int out = 0,flag = 1;char c = getchar();
        while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
        while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
        return flag * out;
        }
    const int maxn = (1 << 19), INF = 1e9;
    int map[19][19];
    int from[19];
    int num, nr, leave;
    int ans = INF;
    bool in[19];
    int cnt, lev[19];
    int vis[19];
    void dfs(int tot, int begin1, int begin2){
    	if(cnt == num){ans = min(ans, tot);return ;}
    	if(tot >= ans)return ;
    	REP(i, begin1, cnt){
    		int u = vis[i];
    		if(lev[u] * leave + tot >= ans)return ;
    		REP(v, begin2, num){
    			if(!in[v] && map[u][v] != INF){
    				vis[++cnt] = v;
    				in[v] = 1, lev[v] = lev[u] + 1;
    				leave -= map[from[v]][v];
    				dfs(tot + map[u][v] * lev[u], i, v + 1);
    				cnt--;
    				in[v] = 0, lev[v] = 0;
    				leave += map[from[v]][v];
    				}
    			}
    		begin2 = 1;
    		}
    	}
    int main(){
        num = RD(), nr = RD();
        REP(i, 1, num)REP(j, 1, num)map[i][j] = INF;
        REP(i, 1, nr){
            int u = RD(), v = RD(), dis = RD();
            map[u][v] = map[v][u] = min(map[u][v], dis);
            }
        REP(i, 1, num){
            int minn = INF;
            REP(j, 1, num){
                if(map[j][i] < minn){
                    minn = map[j][i];
                    from[i] = j;//找一个最短的来自点作为最优预算
                    }
                }
            leave += minn;
            }
        REP(i, 1, num){
            in[i - 1] = 0, lev[i - 1] = 0;
            in[i] = 1, lev[i] = 1;
            leave -= map[from[i]][i];
            vis[cnt = 1] = i;
            dfs(0, 1, 1);
            leave += map[from[i]][i];
            }
        printf("%d
    ", ans);
        return 0;
        }
    
  • 相关阅读:
    Design Pattern Explained
    StringBuilder or StringBuffer
    Algorithms
    Difference between pages and blocks
    Date Time Calendar
    Math if fun
    Sublime Text
    Java Regex
    Learning C
    跨域通信/跨域上传浅析
  • 原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9838590.html
Copyright © 2020-2023  润新知