zoukankan      html  css  js  c++  java
  • NOIP2017提高组Day2T2 宝藏 洛谷P3959 状压dp

    原文链接https://www.cnblogs.com/zhouzhendong/p/9261079.html

    题目传送门 - 洛谷P3959

    题目传送门 - Vijos P2032

    题意

      给定一个 $n$ 个节点 $m$ 条边的无向图。

      现在请你在这个图之上生成一个有根树。

      记 $d_i$ 为节点 $i$ 的深度 $(d_{root}=0)$ ,记 $fadis_i$ 为节点 $i$ 到其父亲节点的连边中的最小边权。

      则这棵树的代价为

    $$sum_{i=1}^{n}(d_i imes fadis_i)$$

      问所有生成树中最小代价为多少。

      $1leq nleq 12,0leq mleq 1000, 边权leq 500000$

    题解

      我们记 $dp[x][d][s]$ 表示以 $x$ 为子树根,其深度为 $d$ ,要在该子树内完成集合 $s$ 中节点的连接,所需要花费的最小代价。

      则显然可以写出 DP 方程:

      (其中 $g[x][y]$ 代表 $x$ 到 $y$ 的最小边权)

    $$dp[x][d][s]=min(dp[y][d+1][s1 ackslash {y}]+g[x][y] imes (d+1)+dp[x][d][s-s1]igg| s1subset s)$$

      至于集合 $s,s2$ 我们可以状压表示。

      现在我们来分析一下时间复杂度。

      首先我们看到前两维以及枚举 $y$ ,每一维一个 $O(n)$。

      最重要的是最后一维。

      这维的复杂度要和剩下的转移复杂度一起算,因为转移复杂度与这一维的数字有关。

      如果这一维集合 $|s|=i$ ,则有 $2^i$ 种子集。满足 $|s|=i$ 的 $s$ 有 $inom{n}{i}$ 个。

      所以这两部分总的复杂度为:(其中要用到:二项式定理

    $$egin{eqnarray*}sum_{i=0}^{n}inom{n}{i}2^i&=&sum_{i=0}^{n}inom{n}{i}2^i imes 1^{n-i}\&=&(1+2)^n\&=&3^n end{eqnarray*}$$

      所以总的复杂度为 $O(n^33^n)$ ,注意常数大会被卡。

      写到这里不禁让我想起某猪。某猪他去年联赛当场写出 $O(n^23^n)$ 的做法,而我至今做出了这个做法,却懒得去做更好的。

      写到这里不禁让我想起某猪。Orz

      写到这里不禁让我想起某猪。我的洛谷本题提交记录的最前面永远的留下了他的代码,永远的留下了当年赌NOIP分数吃全家桶的记忆……

      写道这里,我不禁想起当年那些又吵又闹有骂有笑有他和他的开心的时光。

      写到这里,我又想起了当年天真的笑容们。过去的都过去了,他是否仍然是他?但愿如此,愿他一路顺风。

      不写下去了,不再憋着那泪,空自伤心罢。

    代码

      !!!!!本代码在洛谷被卡常,需要开 $O2$ 才可以通过。!!!!!

    #include <bits/stdc++.h>
    using namespace std;
    const int N=12,S=1<<N;
    int n,m,g[N][N];
    int s,sit[S][S],t[S];
    int dp[N][N][S];
    int DP(int x,int d,int s){
    	int &v=dp[x][d][s];
    	if (~v)
    		return v;
    	if (s==0)
    		return v=0;
    	v=1e9;
    	for (int i=1;i<t[s];i++){
    		int s1=sit[s][i],a=1e9;
    		for (int j=0;j<n;j++)
    			if (((s1>>j)&1)&&g[x][j]<1e9)
    				a=min(a,DP(j,d+1,s1^(1<<j))+(d+1)*g[x][j]);
    		v=min(v,a+DP(x,d,s^s1));
    	}
    	return v;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for (int i=0;i<n;i++)
    		for (int j=0;j<n;j++)
    			g[i][j]=1e9;
    	while (m--){
    		int a,b,c;
    		scanf("%d%d%d",&a,&b,&c),a--,b--;
    		g[a][b]=g[b][a]=min(g[a][b],c);
    	}
    	s=1<<n;
    	for (int i=0;i<s;i++)
    		for (int j=0;j<s;j++)
    			if ((i|j)==i)
    				sit[i][t[i]++]=j;
    	memset(dp,-1,sizeof dp);
    	int ans=1e9;
    	for (int i=0;i<n;i++)
    		ans=min(ans,DP(i,0,(s-1)^(1<<i)));
    	printf("%d",ans);
    	return 0;
    }
    

      

  • 相关阅读:
    数据库原理分析
    数据库常见索引解析(B树,B-树,B+树,B*树,位图索引,Hash索引)
    数据库索引、B树、B+树
    列存储索引
    比较全面的gdb调试命令
    SQLSERVER如何查看索引缺失
    VIM 实现tab标签页及分屏,切换命令
    查看指定spid的脚本当前运行情况和状态
    通过 sysprocesses 简单查询死锁及解决死锁办法
    ASP.NET Core开源地址
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/9261079.html
Copyright © 2011-2022 走看看