1500. Pass Licenses

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");

    }

}

  

 

你可能感兴趣的:(500)