P3959 宝藏

P3959 宝藏

又是毒瘤的noip2017年的原题。

考场上太菜了,prim就得了20分。呜呜呜


\(n\)这么小,不状态都对不起这道题呀

原本,我是想的是,dfs搜索,状压记录状态。

发现,这个东西,一个状态会被重复刷新。不满足无后效性。

然后,考虑按照层数拓展,一层拓展上若干个点。

恩,无后效性解决了。可是这个复杂度看起来是\((2^n\cdot 2^n)\)的呀?

不会爆么?

问题的本质是什么?

问题的本质是问题的本质......

是从一个集合中选出一个子集来,然后再选出与第一个子集没有交集的子集。

假如说我们第一次要选出\(x\)个元素的子集。显然有\(C_n^x\)种选法。

剩下的如何去选呢? 更显然,有\(2^{n-x}\)种选法。

总的方法就是\(C_n^x\cdot 2^{n-x}\)

把所有的关于x的式子加起来,然后利用二项式定理。发现变成了\(3^n\)

然后再按照状态进行转移。总时间复杂度为\(3^nn^3\)看似是七千多万,然鹅都较多状态是没有用的。

然后时间复杂度就成立\(O(\text{能过})\)

存图的话,邻接矩阵就行了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<vector>
using std::min;
const int N=12;
int M[N][N];
int f[(1<<N)+1][N+1];
int dis[N];
int main()
{
    memset(f,-1,sizeof(f));
    memset(M,-1,sizeof(M));
    int n,m;
    scanf("%d%d",&n,&m);
    int a,b,c,all=(1<<n)-1;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        a--;b--;
        if(M[a][b]==-1) M[a][b]=0x7fffffff;
        M[a][b]=M[b][a]=min(M[a][b],c);
    }
    for(int i=0;i<n;i++)    f[1<<i][1]=0;
    for(int i=1;i<(1<<n);i++)
    {
        int opp=all^i;
        for(int j=opp;j;j=(j-1)&opp)
        {
            memset(dis,-1,sizeof(dis));
            bool F=false;
            for(int k1=0;k1<n;k1++) if((1<<k1)&i)
                for(int k2=0;k2<n;k2++) if((1<<k2)&j)
                {
                    if(M[k1][k2]==-1)   continue;
                    if(dis[k2]!=-1)
                        dis[k2]=min(dis[k2],M[k1][k2]);
                    else
                        dis[k2]=M[k1][k2];
                }
            int tot=0,pas;
            for(int k=0;k<n;k++)
                if((1<<k)&j)
                {
                    if(dis[k]==-1)  {F=true;break;}
                    tot+=dis[k];
                }
            if(F)   continue;
            pas=tot;
            for(int k=1;k<=n;k++)
            {
                if(f[i][k]!=-1)
                {
                    if(f[i|j][k+1]==-1)
                        f[i|j][k+1]=0x7fffffff;
                    f[i|j][k+1]=min(f[i|j][k+1],f[i][k]+pas);
                }
                pas+=tot;
            }
        }
    }
    int ans=0x7fffffff;
    for(int i=0;i<=n;i++)
        if(f[all][i]!=-1)
            ans=min(ans,f[all][i]);
    printf("%d",ans);
}

posted @ 2018-10-30 09:54  Lance1ot  阅读(161)  评论(0编辑  收藏  举报