zoukankan      html  css  js  c++  java
  • P3959 宝藏

    P3959 宝藏

    又是毒瘤的noip2017年的原题。

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


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

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

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

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

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

    不会爆么?

    问题的本质是什么?

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

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

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

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

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

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

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

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

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

    #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);
    }
    
    
  • 相关阅读:
    HVIE、HDFS常用操作
    Xshell中使用小键盘问题
    配置SSH免密登录及常见问题
    Linux命令行常用光标控制快捷键
    Linux禁止root用户ssh登录
    sqoop 1.4.7 单机安装
    Hive单机服务的安装配置
    Hadoop 2.9单机安装配置
    CentOS连接wifi
    Servlet
  • 原文地址:https://www.cnblogs.com/Lance1ot/p/9875083.html
Copyright © 2011-2022 走看看