luogu 3959 宝藏

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 }
View Code
原文地址:https://www.cnblogs.com/yyc-jack-0920/p/7875301.html