• NOIP2017 D2T2 宝藏


    洛谷P3959

    其实就是一道暴力搜索题……只是需要一个状态压缩的剪枝比较难想而已

    这根本不叫dfs!只是一个递归而已……开始就被dfs坑了

    思路:

    首先一个基本的预处理

    数据范围n≤12,m≤5000,说明肯定有很多没用的边,在读入的时候预处理掉就可以了,另外n很小可以用邻接矩阵存图访问快

    解法一:暴力搜索

    直接暴力搜索,开一个数组记录一个点有没有被访问过。每到一个状态,从访问过的点到未访问过的点引边算代价,递归枚举一下即可。但此方法时间复杂度较大,大约为O(n^n)?(没有仔细算),n<=8差不多能过

    期望得分:70分(然而实际得分只有60分)

    解法二:状态压缩剪枝

    解法一很显然有很多无用的操作,那么如何进行剪枝呢?

    答案是:状态压缩(这也是博主第一次接触到这个)

    状态压缩就是用一个01串表示一个状态

    每一位0代表未选择,1代表被选择

    实际操作中我们通常用二进制数来表示状态压缩,为了方便我们用倒序(最右边是第一个,紧挨着是第二个,以此类推)

    比如01串010001就可以表示第1个、第5个元素被选择了

    然后使用位运算加速

    选择某个元素(加入状态):x=x|(1<<(i-1))

    判断第i个元素有没有选择:x&(1<<(i-1))(真:被选择;假:未选择)

    那么,开一个数组f[i]表示当前根节点下状态为i的已搜索到的最小代价

    这样我们在搜索时,如果这一次更新的代价比当前状态下以搜索到的最小代价要大(更新的代价>f[i]),说明这次搜索一定不是最优解,就不用进行递归算到最后

    剪枝到底能减掉多少我不知道……但这一题可以AC了

    期望得分:100分

    AC代码:

     1 #include<cstdio>
     2 #include<climits>
     3 #include<cstring>
     4 using namespace std;
     5 int f[10000];//状态压缩答案存储 
     6 int tu[13][13];//邻接矩阵存图 
     7 int n,m;//点、边数 
     8 int depth[13];//每个点深度 
     9 void find(int x)
    10 {
    11     for(int i=1;i<=n;i++)
    12         if(x&(1<<(i-1)))//编号为i的点访问过,从它开始更新 
    13             for(int j=1;j<=n;j++)
    14                 if((!(x&(1<<(j-1))))&&tu[i][j]!=INT_MAX)//编号为j的点未访问过且i,j间有边相连 
    15                     if(f[x]+depth[i]*tu[i][j]<f[x|(1<<(j-1))])//连i,j后答案比之前找出答案小则继续寻找,否则一定不是最优解,不用递归 
    16                     {
    17                         f[x|(1<<(j-1))]=f[x]+depth[i]*tu[i][j];
    18                         depth[j]=depth[i]+1;
    19                         find(x|(1<<(j-1)));
    20                     }
    21 }
    22 int min(int a,int b){return a<b?a:b;}
    23 int main()
    24 {
    25     for(int i=1;i<=12;i++)
    26         for(int j=1;j<=12;j++)
    27             tu[i][j]=INT_MAX;
    28     scanf("%d%d",&n,&m);
    29     for(int i=1;i<=m;i++)
    30     {
    31         int u,v,w;
    32         scanf("%d%d%d",&u,&v,&w);
    33         tu[u][v]=tu[v][u]=min(tu[u][v],w);//存最小边 
    34     }
    35     int ans=INT_MAX;//保存答案 
    36     for(int i=1;i<=n;i++)//对每个点暴力枚举 
    37     {
    38         for(int j=1;j<=(1<<n)-1;j++)
    39             f[j]=INT_MAX;//每一个根节点都要初始化f[] 
    40         f[1<<(i-1)]=0;
    41         depth[i]=1;//根节点深度为1 
    42         find(1<<(i-1));
    43         ans=min(ans,f[(1<<n)-1]);
    44     }
    45     printf("%d
    ",ans);
    46     return 0;
    47 }
  • 相关阅读:
    1619: 【例 1】Prime Distance
    Apache的架构师们遵循的30条设计原则(转帖)
    DP秒思维
    dsu on tree入门
    莫队入门
    行列式与霍尔基夫矩阵
    高精度板子
    splay入门
    回文自动机入门
    AC自动机
  • 原文地址:https://www.cnblogs.com/LiHaozhe/p/9497411.html
Copyright © 2020-2023  润新知