http://acm.timus.ru/problem.aspx?space=1&num=1500
bfs 状态压缩 对应K N M
有 1<<K 个状态代表那种类型的边用 或不用
对每个状态就行bfs求解最短路 若可以搜到或者所求值一定不是最优 则包含这个状态的状态就不用求了 这个剪枝很重要
代码及其注释:
#include<iostream> #include<stdio.h> #include<string.h> #include<math.h> #include<algorithm> #include<vector> #include<set> #include<map> #include<string> #include<queue> #include<stack> #include <iomanip> using namespace std; #define LL long long #define sint short int const int INF=0x3f3f3f3f; //priority_queue<int,vector<int>,greater<int> >qt; const int N=31; int side[N][N];//两个点之间有哪几种边 用状态压缩表示 bool can[1<<20];//这种状态要不要求 int qu[N];//队列 int ans,K;//最优个数 和状态 bool to[N];//广搜时看能不能到底某个点 int n,m,k; bool bfs(int w)//返回true 则包含这个状态的状态就不用搜了 { int sum=0; for(int i=0;i<k;++i) if(w&(1<<i)) ++sum; if(sum>=ans)//一定不是最优 return true; memset(to,false,sizeof(to)); to[0]=true; qu[0]=0; int L=0,R=1; while(L<R) { int x=qu[L++]; for(int i=1;i<n;++i) { if(!to[i]&&(w&side[x][i])) { if(i==1) {ans=sum;K=w;return true;}//更新答案 to[i]=true; qu[R++]=i; } } } return false; } int main() { //freopen("data.in","r",stdin); while(scanf("%d %d %d",&k,&n,&m)!=EOF) { memset(can,true,sizeof(can)); memset(side,0,sizeof(side)); while(m--) { int l,r,w; scanf("%d %d %d",&l,&r,&w); side[l][r]=(side[l][r]|(1<<w));//记录两点之间有哪几种边 side[r][l]=(side[r][l]|(1<<w)); } ans=INF; int h=(1<<k)-1; for(int i=1;i<=h;++i) { if(can[i]) { if(bfs(i)) { for(int l=i;l<=h;l=((l+1)|i))//把包含这个状态的状态标记 不用再求 can[l]=false; } } } printf("%d\n",ans); bool first=true; for(int i=0;i<k;++i) if(K&(1<<i)) { if(first) {printf("%d",i);first=false;} else printf(" %d",i); } printf("\n"); } }