《P3959 [NOIP2017 提高组] 宝藏》
这题数据量很小,显然是状压和dfs。
dfs + 剪枝也可以过这题,但是剪枝比较玄学。。
状压dp:
f[i]表示选了的点状态为i时的最小总代价。
dep[i][j]表示选点状态为i的最优状态时的,j的深度。
因为这题多余的边不用连,显然最终的答案是一棵树。
每次选定两条边加入即可。注意的是i 向 j转移,那么他的相对最优深度也要全部转移。
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 1e4 + 5; const int M = 3e5 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 0x3f #define dbg(ax) cout << "now this num is " << ax << endl; namespace FASTIO{ inline LL read(){ LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } } using namespace FASTIO; int dis[15][15],f[1 << 12],dep[1 << 12][15],n,m; int solve(int x){ memset(f,INF,sizeof(f)); memset(dep,INF,sizeof(dep)); dep[1 << (x - 1)][x] = 1; f[1 << (x - 1)] = 0; for(int i = 0;i < (1 << n);++i){ for(int j = 1;j <= n;++j){ if(((i >> (j - 1)) & 1) == 0) continue;//要满足j在i里面 for(int k = 1;k <= n;++k){ if(((i >> (k - 1)) & 1) == 1) continue;//要满足k不在i里面 if(dis[j][k] == INF) continue;//没边 if(1LL * f[i | (1 << (k - 1))] > 1LL * f[i] + 1LL * dep[i][j] * dis[j][k]){ f[i | (1 << (k - 1))] = f[i] + dep[i][j] * dis[j][k]; memcpy(dep[i | (1 << (k - 1))],dep[i],sizeof(dep[i | (1 << (k - 1))])); dep[i | (1 << (k - 1))][k] = dep[i][j] + 1; } } } } return f[(1 << n) - 1]; } int main() { n = read(),m = read(); memset(dis,INF,sizeof(dis)); while(m--){ int u,v,w;u = read(),v = read(),w = read(); dis[u][v] = dis[v][u] = min(dis[u][v],w); } int ans = 1000000000;//这里用了INF就会变成63.. for(int i = 1;i <= n;++i){ ans = min(ans,solve(i)); } printf("%d\n",ans); system("pause"); return 0; }