1. 程式人生 > >寶藏(NOIP2017)

寶藏(NOIP2017)

題目分析:

    考場上,這個只搞了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;
}