状态压缩.宝藏

第一篇博客的解法太复杂了
我们发现很多深度相同的点分开计算导致这个状态转移方程如此复杂
并且转移的结构不够优秀,状态之间的关系不是特别明显

我们考虑一个最优解
显然这是一棵生成树,如果我们能构造出来 g[i][j] g [ i ] [ j ] 表示从i状态挖到j状态的最小花费
我们就可以按照层次加入点集更新答案了。

f[i][j] f [ i ] [ j ] 表示当前深度为 i i ,状态为 j j 的最小花费
f[i][s]=f[i1][t]+g[s][t](i1) f [ i ] [ s ] = f [ i − 1 ] [ t ] + g [ s ] [ t ] ∗ ( i − 1 )

时间复杂度O(3^n*n)
如果预处理不够优秀的话O(4^n)

预处理我们再开一个数组 c[s][i] c [ s ] [ i ] 表示状态 s s 挖到点 i i 的最小花费(不考虑深度)
然后我们用边权更新 c c 数组,再用 c c 数组更新 g g 数组即可

这样代码会比第一种好写很多

#include
using namespace std;

const int maxn=15;
const int maxs=(1<<12)+100;
const int oo=1000000000;

int e[maxn][maxn];

int h[maxn][maxs];
int g[maxs][maxs];
int f[maxn][maxs];

int n,m;

int main()
{

    scanf("%d%d",&n,&m);
    for (int i=1; i<=n; i++)
        for (int j=1; j<=n; j++) e[i][j]=oo;
    for (int i=1; i<=m; i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        e[a][b]=min(e[a][b],c);
        e[b][a]=min(e[b][a],c);
    }

    int ms=1<for (int i=1; i<=n; i++)
        for (int s=1; sif ( !(s&(1<<(i-1))) )
                for (int j=1; j<=n; j++)
                    if ( s&(1<<(j-1)) )
                        h[i][s]=min(h[i][s],e[i][j]);
        }

    for (int s=1; sint t=s&(s-1);
        while (t)
        {
            int x=s^t;
            for (int i=1; i<=n; i++)
                if ( x&(1<<(i-1)) )
                {
                    g[s][t]+=h[i][t];
                    if (g[s][t]>oo) g[s][t]=oo;
                }
            t=s&(t-1);
        }
    }

    for (int i=1; i<=n; i++)
        for (int s=0; sfor (int i=1; i<=n; i++) f[1][1<<(i-1)]=0;
    for (int i=2; i<=n; i++)
        for (int s=1; sint t=s&(s-1);
            while (t)
            {
                int temp;
                if (g[s][t]1);
                else temp=oo;
                if (f[i-1][t]1][t]+temp);
                t=s&(t-1);
            }
        }

    int ans=oo;
    for (int i=1; i<=n; i++) ans=min(ans,f[i][ms-1]);
    printf("%d\n",ans);

    return 0;
}

你可能感兴趣的:(状态压缩.宝藏)