"NOIP2017" Treasure

"NOIP2017" Treasure solution to a problem

Read the blog better results


He went to the annualNOIPCSP-S before the review time to do Zhenti

So I met this question


First, the observed data range \ (. 1 \ n-Le \ Le 12 is \) , then the possibility is great pressure like \ (\ texttt {DP} \ ) or \ (\ texttt {DFS} \ ) burst search

However, since this problem in the \ (\ texttt {DP} \ ) list which, like pressure then priority

Simplify the meaning of the questions:

From a given \ (n-\) points, \ (m \) of edges to heavy edge undirected connected graph, find a spanning tree, so that the topic for the Minimum Value of

From the construction side given the value of the subject, we find that the value of an edge with the following points related to:

  • Root position
  • Height of the tree in its current state
  • The length of the edge

Length of a side can not be changed, the position of the root and not well as \ (\ texttt {DP} \ ) stage time, we consider the height of the tree as DP stage

Root depth set to 1

Set \ (\ texttt {f [i ] [j]} \) represents the current height of the tree is \ (I \) , have been selected from a set of points is set \ (J \) , then the state transition equation is the
\ [f [i] [j] = \ min_ {k \ \ in \ j} (f [i-1] [j \ \ mathrm {xor} \ k] + dis [j \ \ mathrm {xor} \ k] [k] \ cdot (i-
1)) \] wherein XOR operation here is to take the complement means, \ (\ texttt {DIS [I] [J]} \) represents the \ (I \) this is selected set point plus the next level will be selected \ (j \) minimum cost required to set this point

Then we should do to improve \ (\ texttt {dis} \ ) array it

First recursive formula given
\ [dis [i] [j ] = dis [i] [j \ \ mathrm {xor} \ \ mathrm {lowbit} (j)] + \ min_ {k = 1} ^ {n \ \ & \ & \ (1 <<
k) \ & i} (d [\ log_2 \ mathrm {lowbit} (j) +1] [k]) \] where \ (D \) array represents \ (I \) points to the second \ (J \) length of road points (if none \ (\ infty \) ), \ (J \) of \ (I \) of any subset of the complement

Then from small to large enumeration \ (j \) , will be able to ensure the correct order (because \ (j \ \ mathrm {xor } \ \ mathrm {lowbit} (j) \) must be better than \ (j \) is smaller)

Because each update only involves changing a point, so it is not difficult to come pretreatment \ (dis \) array correctness

Then, this problem would be finished

In addition there are several points to note

  • Side is best to use an adjacency matrix storage, because there is heavy side, but please do not assign initial values ​​too high, this will lead to the same time in dynamic Solver overflow, resulting in erroneous answers

  • If you follow the above simple approach to solving the kind of complexity may not be able to withstand, we observed a lot of unnecessary enumeration of subsets, so we can change a way:

    for(int i=S;i;i=(i-1)&S)

    In this case all \ (I \) it must be \ (S \) subset

    Konjac understanding: is not equal to \ (S \) a \ (S \) subset must be in \ ([0, S) \ ) in

    Then can be determined in this OR operation among the decimal largest subset of the number , which is set \ (P \) , then all the rest represents a decimal smaller than he subset in \ ([0, P) \ ) which , and so on solving nature can get all the subsets

  • A little bit about the state of optimization

    Easy to find when the tree height \ (I \) , the at least \ (I \) nodes, all less than the midpoint of the number of states \ (I \) (i.e. the bits \ (1 \) of less than the number \ (I \) ), and can not enumerate all subsets, skip, time complexity for which there has been furtherconstantoptimization. This may be obtained by pretreatment.

Finally, paste the code, variable names mentioned above are slightly different

#include<bits/stdc++.h>
using namespace std;
const int maxn=12;
int d[maxn+5][maxn+5];
int g[maxn+5][(1<<maxn)+5];
int f[(1<<maxn)+5][(1<<maxn)+5];
int lg[(1<<maxn)+5];//懒 
int q[(1<<maxn)+5],cnt;
int sum[(1<<maxn)+5];
int main()
{
    memset(g,63,sizeof g);
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;++i)
        lg[1<<i]=i;//预处理,因为懒
    for(int i=0;i<12;++i)
        for(int j=0;j<12;++j)
            d[i][j]=1000000;//赋最大值
    for(int i=1;i<=m;++i)
    {
        int a,b,c;
        cin>>a>>b>>c;
        --a,--b,d[a][b]=d[b][a]=min(d[a][b],c); 
    }
    int x,S=(1<<n)-1;//全集定义
    for(int i=1;i<=S;++i)
    {
        x=i;
        while(x) x&=(x-1),++sum[i];
    }//预处理每一个状态上点的个数
    for(int i=1;i<=S;++i)
    {
        cnt=0;
        for(int j=S^i;j;j=(j-1)&(S^i)) q[++cnt]=j;//由于这样做子集的顺序是从大到小的,不符合DP的顺序,所以要逆序 
        for(int j=cnt;j>=1;--j)
        {
            int u=lg[q[j]&-q[j]],e=1000000;
            for(int v=0;v<n;++v)
                if(1<<v&i) e=min(d[u][v],e);
            f[i][q[j]]=f[i][q[j]^(q[j]&-q[j])]+e;
        }
    }
    for(int i=0;i<n;++i) g[1][1<<i]=0;//初始状态
    for(int i=2;i<=n;++i)
        for(int j=(1<<i)-1;j<=S;++j)//剪枝,这里i的初始状态跳过了肯定不符合的状态
        {
            if(sum[j]<i) continue;//剪枝,不满足直接跳过
            for(int k=j;k;k=(k-1)&j)
                g[i][j]=min(g[i][j],g[i-1][j^k]+f[j^k][k]*(i-1));
        }
        int ans=(1<<30);
    for(int i=1;i<=n;++i) ans=min(ans,g[i][S]);//取最小值
    cout<<ans<<endl;
    return 0;
}

Guess you like

Origin www.cnblogs.com/HenryHuang-Never-Settle/p/11529900.html