1. 程式人生 > >Uva 1040 狀壓+搜尋 2005 ACM world final problem c

Uva 1040 狀壓+搜尋 2005 ACM world final problem c

題目的隱含條件將這道題指向了最小生成樹;

利用類似prim的方法,列舉所有子圖並判斷是否包含詢問點,如果包含那麼可以更新答案;

邊統計邊更新,且由於更新一定是向更多的點狀態下更新,所以一定可以統計到答案,不至於到全部是inf的情況

再更新答案時記錄ps,pe兩個變數分別表示此狀態最後一次更新前的狀態,邊,會在尋找路徑時用到

最後統計到的答案ans,走到初始的t,路徑中打下vis標記後再從頭dfs沿著vis打過的走下去,並在路徑中遇到葉子節點時順便將走過的路徑放入vector

最後利用vector輸出即可

#include<bits/stdc++.h>
#define
rep(i,x,y) for(register int i=x;i<=y;i++) #define dec(i,x,y) for(register int i=x;i>=y;i--) using namespace std; inline int read(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;} const int N=25; const int M=500; const int inf=0x3f3f3f3f; vector<int> v[N]; int n,t,m,q,x,all,ans,mx,ask[N]; int dp[1<<21],ps[1<<21],pe[1<<21]; bool vis[N]; int head[N],tot=1; struct node{int v,w,next;bool vis;}e[N*N<<1]; void insert(int u,int v,int w){ e[
++tot]=(node){v,w,head[u],0};head[u]=tot; e[++tot]=(node){u,w,head[v],0};head[v]=tot;} inline int count(int x){ int ans=0;while(x) ans++,x-=x&(-x);return ans;} int s[N],top; void dfs(int u,int f){ s[++top]=u; if(vis[u]){ dec(i,top,1) v[u].push_back(s[i]);} //將目前的記錄順序放入vector for(int i=head[u];i;i=e[i].next){ int v=e[i].v; if(v==f||e[i].vis==0) continue; dfs(v,u); }--top; } int main(){ freopen("travel.in","r",stdin); freopen("travel.out","w",stdout); n=read(),t=read(),m=read(); rep(i,1,m){int u=read(),v=read(),w=read();insert(u,v,w);} q=read();rep(i,1,q) x=read(),all|=(1<<(x-1)),vis[x]=1,ask[i]=x; //all 記錄全部的詢問點的集合 memset(dp,inf,sizeof dp);dp[1<<(t-1)]=0;ans=inf; //利用含t的集合更新答案,最後包含的集合一定有t rep(i,0,(1<<n)-1){ if((i&all)==all){//更新答案的集合包含 if(dp[i]<ans){ ans=dp[i];mx=i; }else if(dp[i]==ans){ if(count(i)<count(mx))//比較集合點數多少 mx=i; }continue; } for(int j=1;j<=n;j++)if((1<<(j-1))&i){ for(int k=head[j];k;k=e[k].next){ //沿著邊更新答案 int v=e[k].v,w=e[k].w; if((1<<(v-1))&i) continue; if(dp[i|(1<<(v-1))]>dp[i]+w) dp[i|(1<<(v-1))]=dp[i]+w, ps[i|(1<<(v-1))]=i,pe[i|(1<<(v-1))]=k; //ps代表i上一個狀態轉移來的集合(點集), //pe代表 i上一次轉移的邊 } } } printf("distance = %d\n",ans); //利用已有標記爬回去 while(mx!=(1<<(t-1))) e[pe[mx]].vis=1,mx=ps[mx]; dfs(t,0);//再正著下來,記錄順序,放入vector rep(i,1,q){ for(int j=0;j<v[ask[i]].size()-1;j++) printf("%d-",v[ask[i]][j]); printf("%d\n",v[ask[i]][v[ask[i]].size()-1]);} return 0;}

模擬賽搬的就是這道,本地A了但是提交UVA wa掉了,OJ實在太麻煩所以就咕咕咕了(喵喵喵),如果有ctrl c的同學可能會比較慘,不怪我了...haha

完結撒花