noip2017 D2T2 宝藏 某zz选手看到数据范围直接就dfs了,骗到了70分
题目大意:
n宝藏屋,已知n 个宝藏屋之间可供开发的 m 条道路和它们的长度
选择一个宝藏屋作为起点
新开发一条道路的代价是:L×K
L代表这条道路的长度,K代表从起点宝藏屋到这条道路起点的宝藏屋所经过的宝藏屋的数量(包括起点宝藏屋和这条道路起点的宝藏屋)
选定起点宝藏屋和之后开凿的道路,使得工程总代价最小,并输出这个最小值
思路:
看到n为12本来想到状压dp的,但是以为dfs剪枝能过(大佬们的dfs可以过)
然后就是状态dp[i][j] 其中i表示所有点的最大深度,j表示联通状态
对于每个状态可以枚举j的补集的子集,然后枚举点把他们连起来
转移就行了
具体见注释
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstdlib> 5 #include<cstring> 6 #include<algorithm> 7 #include<vector> 8 #include<queue> 9 #define inf 2139062143 10 #define ll long long 11 #define MAXN 10010000 12 #define MOD 13 using namespace std; 14 inline int read() 15 { 16 int x=0,f=1;char ch=getchar(); 17 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 18 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 19 return x*f; 20 } 21 int n,m,map[15][15],dp[15][4500],l[4500]; 22 int lowbit(int x) {return x&(-x);} 23 int main() 24 { 25 n=read(),m=read();int a,b,c,t=(1<<n)-1,rst,tmp,v; 26 memset(dp,127,sizeof(dp)); 27 for(int i=0;i<n;i++) dp[0][1<<i]=0,l[1<<i]=i;//初始化以每一个点作为起点的状态 28 memset(map,127,sizeof(map)); 29 while(m--) 30 { 31 a=read()-1,b=read()-1,c=read(); 32 map[b][a]=map[a][b]=min(map[a][b],c); 33 } 34 for(int i=0;i<n;i++) 35 for(int j=0;j<=t;j++) 36 { 37 if(dp[i][j]==inf) continue; 38 rst=t^j;//rst为当前集合的补集 39 for(int k=rst;k;k=(k-1)&rst)//枚举补集的子集(一个枚举子集的骚操作,具体操作可以脑补) 40 { 41 tmp=0; 42 for(int a=k;a;a-=lowbit(a))//枚举 k(补集的子集 )里的每一个点 43 { 44 v=inf; 45 //枚举 j(当前连通块)内的所有点与 k内这个点相连的最小值 46 for(int b=j;b;b-=lowbit(b)) v=min(v,map[l[lowbit(a)]][l[lowbit(b)]]); 47 if(v==inf) break;//如果找不到,说明无法转移到 j|k 这个状态 48 tmp+=v; 49 } 50 if(v==inf) continue;//这个子集不合法 51 tmp*=(i+1); 52 dp[i+1][j|k]=min(dp[i+1][j|k],dp[i][j]+tmp);//把 k和 j连了起来,所以连通块状态为 j|k 53 } 54 } 55 int ans=inf; 56 for(int i=0;i<n;i++) ans=min(ans,dp[i][t]);//枚举把所有点都连起来的不同深度 57 printf("%d",ans); 58 }