JZOJ 5459. 【NOIP2017提高A组冲刺11.7】密室

题目大意

有n个节点m条边,里面有一些不同种类钥匙。如果要通过一条边,需要有特定种类的钥匙(多把)。问最少要经过几条边从1走到n。
数据范围
n≤5000,m≤6000,钥匙的种类k≤10。

题解

条件:
①要求通过一条边有特定种类的钥匙。
②经过的边数最少。
特定的种类?时间复杂度肯定含有2^k,再套什么最短路算法比如spfa。
直接状压DP!!!
但是为什么时间超限?我蠢。
由于队列里已经记录了点x和具备钥匙的状态s,直接拿它去更新其他状态就好了,不必枚举冗余状态

错误代码(TLE)

while(H<T){
    H++;
    x=qu[H][0];
    kk=qu[H][1];
    for(i=head[x];i;i=edge[i].next){
        fo(s,0,_2[k+1]-1){
            v=s|a[x];
            if((v|edge[i].val)!=v)continue;
            if(f[edge[i].to][v]>f[x][s]+1){
                f[edge[i].to][v]=f[x][s]+1;
                if(!bz[edge[i].to][v]){
                    bz[edge[i].to][v]=1;
                    qu[++T][0]=edge[i].to;
                    qu[T][1]=v;
                }
            }
        }
    }
    bz[x][kk]=0;
}

这个代码的缺点:
①枚举大量冗余状态。
②有松弛操作。

但是这道题目不需要松弛操作,因为第一次更新的解一定是状态的最优解。
所以bfs就好了。

总结

①对于spfa,如果已经有二进制的状态,就不必枚举代表意义相同的二进制状态

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define N 5010
#define M 6010
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct note{
    int to,next,val;
};note edge[M];
int head[N],tot;
int f[N][1030];
int i,j,k,ans,n,m,x,kk,temp;
int u,v,H,T,s;
long long sum,cnt;
int _2[12];
int a[N];
int qu[N*3030][2];
void lb(int x,int y,int z){
    edge[++tot].to=y;
    edge[tot].next=head[x];
    edge[tot].val=z;
    head[x]=tot;
}
int main(){
    freopen("room.in","r",stdin);
    freopen("room.out","w",stdout);
    _2[1]=1;fo(i,2,11)_2[i]=_2[i-1]*2;
    scanf("%d%d%d",&n,&m,&k);
    fo(i,1,n){
        a[i]=0;
        fo(j,1,k){
            scanf("%d",&x);
            a[i]+=_2[j]*x;
        }
    }
    fo(i,1,m){
        scanf("%d%d",&u,&v);
        temp=0;
        fo(j,1,k){
            scanf("%d",&x);
            temp+=_2[j]*x;
        }
        lb(u,v,temp);
    }
    memset(f,63,sizeof(f));
    f[1][a[1]]=0;
    H=0,T=1;
    qu[1][0]=1;
    qu[1][1]=a[1];
    while(H<T){
        H++;
        x=qu[H][0];kk=qu[H][1];
        for(i=head[x];i;i=edge[i].next){
            if((kk|edge[i].val)!=kk)continue;
            v=kk|a[edge[i].to];
            if(f[edge[i].to][v]==1061109567){
                f[edge[i].to][v]=f[x][kk]+1;
                qu[++T][0]=edge[i].to;
                qu[T][1]=v;
            }
        }
    }
    ans=1061109567;
    fo(i,0,_2[k+1]-1)ans=min(ans,f[n][i]);
    if(ans==1061109567)printf("No Solution");else
    printf("%d",ans);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值