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

    NOIP 大活题。。吧

    题意:给你一张n个点,m条边的图,求一棵有根生成树,使得除根节点外每个节点的不带权深度(根节点深度为1)与其父边的边权的乘积的和最小。

    看起来是不是有点像最小生成树,那么大胆猜想一波。。??

    很可惜,这不是一个猜测题。。(加上玄学模拟退火好像也可以乱搞过掉)

    那么再看一下n小于等于12,爆搜或状压??

    这里看看状压怎么写:

    首先考虑状态,二进制表示点集就不说了,再看看题目,只涉及一个深度,那么把深度也看做状态就可以了。

    因此设f[i][j]表示点集状态为i,最大深度为j时的最小代价。

    那么转移的话,设x为i的子集,则f[i][j]=min (f[i][j], f[x][j-1]+cost*j)。

    那么现在题就是解决cost了。

    考虑到cost实际上就是所有属于i但不属于x的点,向x连边后构成i的最小边权和。

    所以直接在转移时计算就好了。

    至此,这个问题基本已经解决了。

    小优化:可以提前预处理每个状态能向外连的点集。

    #include <bits/stdc++.h>
    using namespace std;
    
    #define INF 0x3f3f3f3f
    #define Min(a,b) a>b?b:a
    
    const int N=13;
    const int Mx=4100;
    
    int n,m,All,Ans,dis[N][N],g[Mx],f[Mx][N];
    
    inline void New_ () {
        memset (f, 0x3f, sizeof (f) ); 
        memset (dis, 0x3f, sizeof (dis) );
    }
    
    int main () {
        std::ios::sync_with_stdio (false);
        New_ ();
        cin >> n >> m;
        for (int i=1, x, y, z;i<=m;++i) {
            cin >> x >> y >> z;
            x--, y--;
            //方便状态转移
            dis[x][y]=dis[y][x]=Min (dis[x][y], z);
        }
        int All=1<<n;
        for (int i=1;i<All;++i)
            for (int j=0;j<n;++j)
                if ( ( (1<<j) | i) ==i) {
                    dis[j][j]=0;
                    for (int k=0;k<n;++k)
                        if (dis[j][k]!=INF)
                            g[i]|= (1<<k);
                }
        for (int i=0;i<n;++i) f[1<<i][0]=0;
        for (int i=2;i<All;++i)
            for (int s=i-1;s;s= (s-1) &i)  
                if ( (g[s] | i) ==g[s]) {
                    // s必须能转移到i
                    int Cost=0, ss=s^i, pay;
                    for (int k=0;k<n;++k) {
                        if ( ( (1<<k) & ss) !=0) {
                            pay=INF;
                            for (int j=0;j<n;++j)
                                if ( ( (1<<j) & s) !=0)
                                    pay=Min (pay, dis[j][k]);
                            Cost+=pay;
                        }
                    }
                    for (int j=1;j<n;++j)
                        if (f[s][j-1]!=INF)
                            f[i][j]=min (f[i][j], f[s][j-1]+Cost*j);
                }
        Ans=INF;
        for (int i=0;i<n;++i) Ans=min (Ans, f[All-1][i]);
        cout << Ans << endl;
        return 0;
    }
    BY BHLLX
  • 相关阅读:
    appium之adb常用命令
    测试基础之等价类
    selenium之CSS定位
    括号序列的最小代价
    Spark相对于MapReduce的优势
    Cache系统设计
    [京东2017实习生笔试] 终结者C
    [京东2017实习生笔试] 通过考试
    [hihoCoder] 1078. 线段树的区间修改
    [转载] 一步一步理解线段树
  • 原文地址:https://www.cnblogs.com/Bhllx/p/9810355.html
Copyright © 2011-2022 走看看