寶藏(NOIP2017)
阿新 • • 發佈:2019-02-02
題目分析:
考場上,這個只搞了45分,可以有人用A* A了啊。我們對於遍歷過的節點i我們標記為1,對於沒有標記過的節點我們標記為0;然後我們用一串二進位制數來表示當前局面的狀態,用dp[i]表示遍歷完當前二進位制中1的位所表示的節點所用的最小花費。
我們先列舉每一個點作為起點,然後我們開始dfs,我們列舉當前dfs到的狀態所表示的遍歷過的節點,然後我們列舉所有與這些節點相鄰的節點,並且判斷是否可以更新,最後再回溯;
程式碼放上
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #define inf 2147483647 using namespace std; int dp[1<<13],dis[15],G[15][15],isG[15][15],n,m,ans=inf; void Add_(int x,int y,int w) { G[x][y]=w; G[y][x]=w; isG[x][y]=1; isG[y][x]=1;//右邊 } void dfs(int x) { for(int i=1;i<=n;i++)//列舉節點看看當前的狀態是否被遍歷過 { if((1<<(i-1))&x)//被遍歷過了就開始以當前節點為起點進行遍歷 { for(int j=1;j<=n;j++) { if(!(1<<(j-1)&x)&&isG[i][j])//如果有邊並且在dfs到的狀態裡沒有被遍歷 if(dp[1<<(j-1)|x]>dp[x]+dis[i]*G[i][j])//滿足更新,其中| 在二進位制中相當於狀態相加 { int temp=dis[j]; dis[j]=dis[i]+1;//深度加一 dp[1<<(j-1)|x]=dp[x]+dis[i]*G[i][j];//更新 dfs(1<<(j-1)|x);//繼續dfs加上x狀態 dis[j]=temp; } } } } } int main(){ scanf("%d%d",&n,&m); memset(G,63,sizeof(G)); for(int i=1;i<=m;i++) { int x,y,w;scanf("%d%d%d",&x,&y,&w); if(w<G[x][y]) Add_Edge(x,y,w); } for(int i=1;i<=n;i++) { memset(dis,63,sizeof(dis)); for(int j=1;j<=(1<<n)-1;j++)dp[j]=inf; dis[i]=1;dp[1<<(i-1)]=0;//每一次初始化 dfs(1<<(i-1)); ans=min(ans,dp[(1<<n)-1]); } printf("%d",ans); return 0; }