noip2017 Day2 T2 宝藏

题面

题意

给出一副无向图,每条边都有一个权值且均未开通,先可以随便取一个起点,要开通一些边,使它成为一个连通图,开通一条边的代价为权值*起点到它的点的个数(起点也算),求最小代价.

方法

考试时想到的是一种十分暴力的状压dp,用八进制压缩每一个点到起点的距离,复杂度为(8^n),无论是时间还是空间都还是很紧张,但还是顺利地拿到了70分.

正确方法是2^n状压,以起点为根节点建树,一层一层更新,枚举每一层所有的更新方法,用距离它最短的已更新的点去更新(不一定是上一层,因为如果是上一层以上的,则在更新那一层之前时已经更新,不会留到现在).

代码(70)

#include
#include
#include
#include
#include
#define INF 0x3f3f3f3f
using namespace std;

int mm[20][20],n,m,ans,mn,d[20],dp[16777220],eig[]={1,8,64,512,4096,32768,262144,2097152,16777216};

inline void wj()
{
    freopen("treasure.in","r",stdin);
    freopen("treasure.out","w",stdout);
}

inline int qw(int u,int v)
{
    u/=eig[v-1];
    return u%8;
}

inline int dfs(int zt)
{
    if(dp[zt]!=-1) return dp[zt];
    register int i,j,k,res=INF;
    bool have=0;
    for(i=1;i<=n;++i)
    {
        if(qw(zt,i)!=7) continue;
        have=1;
        for(j=1;j<=n;++j)
        {
            k=qw(zt,j);
            if(i==j||k+1>=7||mm[i][j]==INF) continue;
            res=min(res,mm[i][j]*(k+1)+dfs(zt-(6-k)*eig[i-1]));
        }
    }
    if(!have) return 0;
    dp[zt]=res;
    return res;
}

inline void read(int &u)
{
    u=0;
    register char ch=getchar();
    for(;ch<'0';ch=getchar());
    for(;ch>='0'&&ch<='9';ch=getchar())
    {
        u=(u*10+ch-'0');
    }
}

int main()
{
//  wj();
    register int i,p,q,o;
    ans=INF;
    memset(mm,INF,sizeof(mm));
    memset(dp,-1,sizeof(dp));
    read(n),read(m);
    for(i=1;i<=m;++i)
    {
        read(p),read(q),read(o);
        mm[p][q]=mm[q][p]=min(mm[p][q],o);
    }
    for(i=1;i<=n;++i)
    {
        ans=min(ans,dfs(((1 << (n*3))-1)-7*eig[i-1]));
    }
    printf("%d",ans);
}

代码(100)

#include
#include
#include
#include
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;

ll n,m,dp[20][5000],bn[20][20],ans,num[20],num2[20],nn,aa[20],an1[20][5000],an2[20][5000],dee;
bool have;
vector<ll>jl[5000];

void get(ll now,ll sum1,ll sum2)
{
    if(now==nn+1)
    {
        aa[dee]++;
        an1[dee][aa[dee]]=sum1;
        an2[dee][aa[dee]]=sum2;
        return;
    }
    get(now+1,sum1|(1 << (num[now]-1)),sum2+num2[now]);
    get(now+1,sum1,sum2);
}

ll dfs(ll deep,ll zt)
{
    if(zt==(1 << n)-1) return 0;
    if(deep>n) return INF;
    if(dp[deep][zt]!=-1) return dp[deep][zt];
    ll i,j,k,res=INF,mn;
    nn=0;
    for(i=1; i<=n; i++)
    {
        if((1 << (i-1))&zt) continue;
        mn=INF;
        for(k=0; k
        {
            j=jl[zt][k];
            mn=min(mn,bn[i][j]);
        }
        if(mn==INF) continue;
        nn++;
        num[nn]=i;
        num2[nn]=mn;
    }
    aa[deep]=0;
    dee=deep;
    get(1,0,0);
    for(i=1; i<=aa[deep]; i++)
    {
        if(!an1[deep][i]) continue;
        res=min(res,dfs(deep+1,an1[deep][i]|zt)+(deep+1)*an2[deep][i]);
    }
    dp[deep][zt]=res;
    return res;
}

int main()
{
    register ll i,j,p,q,o;
    memset(bn,INF,sizeof(bn));
    memset(dp,-1,sizeof(dp));
    cin>>n>>m;
    for(i=1; i<=(1 << n)-1; i++)
    {
        jl[i].clear();
        for(j=1; j<=n; j++)
        {
            if(i&(1 << (j-1)))
                jl[i].push_back(j);
        }
    }
    for(i=1; i<=m; i++)
    {
        scanf("%lld%lld%lld",&p,&q,&o);
        bn[p][q]=bn[q][p]=min(bn[p][q],o);
    }

    ans=INF;
    for(i=1; i<=n; i++)
    {
        ans=min(ans,dfs(0,(1 << (i-1))));
    }
    printf("%lld",ans);
}

你可能感兴趣的:(dp,树,经典)