【题解】洛谷P3959 [NOIP2017TG] 宝藏(状压DP+DFS)
洛谷P3959:https://www.luogu.org/problemnew/show/P3959
前言
NOIP2017时还很弱(现在也很弱
看出来是DP 但是并不会状压DP
现在看来思路并不复杂 只是存状态有点难想到
思路
因为n最大为12
所以可以想到是状压
因为n<=12 所以可以用邻接矩阵存下图
枚举每个点作为起点开始DFS
注意每次DFS的初始化和赋值问题即可
代码
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define INF 1e9+7 int n,m,ans=INF; int f[8000],h[15],dis[15],map[15][15];//dis为题目中的k 即经过了几个宝藏 bool vis[15][15]; void dfs(int s) { for(int i=1;i<=n;i++) { if(s&(1<<(i-1)))//寻找已经挖通的宝藏 for(int j=1;j<=n;j++)//枚举下一个宝藏 { if(!(s&(1<<(j-1)))&&vis[i][j]&&f[1<<(j-1)|s]>f[s]+dis[i]*map[i][j]) {//如果没有挖过j 且i与j之间有路径 且有一个更优解 int k=dis[j];//记录原dis方便后面回溯 dis[j]=dis[i]+1;//记录新的dis f[1<<(j-1)|s]=f[s]+dis[i]*map[i][j];//更新更优解 dfs(s|1<<(j-1)); dis[j]=k;//回溯 } } } } int main() { cin>>n>>m; memset(map,INF,sizeof(map));//矩阵赋值最大值 for(int i=1;i<=m;i++) { int x,y,z; cin>>x>>y>>z; if(z<map[x][y])//取最小的一条路 { map[x][y]=z; map[y][x]=z; vis[x][y]=1;//判断是否存在路径 vis[y][x]=1; } } for(int i=1;i<=n;i++)//枚举每个点为起点 { memset(dis,INF,sizeof(dis));//初始化 memset(f,INF,sizeof(f)); dis[i]=1;//起点经过的点有1个 f[1<<(i-1)]=0;//当前状态的最小值 dfs(1<<(i-1));//搜索 ans=min(ans,f[(1<<n)-1]);//状态满的时候取最小值 } cout<<ans; }