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");
}
}
浙公网安备 33010602011771号