BZOJ1139: [POI2009]Wie

111 篇文章 0 订阅
5 篇文章 0 订阅

题目大意:大陆上有n个村庄,m条双向道路,p种怪物,k个铁匠,每个铁匠会居住在一个村庄里,你到了那个村庄后可以让他给你打造剑,每个铁匠打造的剑都可以对付一些特定种类的怪物,每条道路上都可能出现一些特定种类的怪物,每条道路都有一个通过所需要的时间,现在要从1走到n,初始的时候你没有剑,要求在经过一条道路的时候,对于任意一种可能出现在这条道路上的的怪物,你都有已经有至少一把剑可以对付他,求从1走到n的最短时间(打造剑不需要时间)


没有很多人做的原因是因为之前题面有问题?

p<=13,那一看就是要记状态了

然后又要求求最短路

第一下想到状压DP,发现没有拓扑序,所以就只好dijkstra了

d[i][j]记录到i号点,现在能打怪的状态是j,最少需要多长时间

转移的时候判断一下这条边能不能走就可以了


虽说200W个点还是有点虚的,不过实际好像跑的还挺快,256ms

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define mp(x,y,z) (make_pair(make_pair(x,y),z))
#define N 6010
using namespace std;
int a[210];
int to[N],nxt[N],pre[210],cnt,t[N],w[N];
void ae(int ff,int tt,int TT,int ww)
{
	cnt++;
	to[cnt]=tt;
	nxt[cnt]=pre[ff];
	pre[ff]=cnt;
	t[cnt]=TT;
	w[cnt]=ww;
}
int d[210][1<<13];
bool vis[210][1<<13];
priority_queue<pair<pair<int,int>,int>,vector<pair<pair<int,int>,int> >,greater<pair<pair<int,int>,int> > >q;
int n;
int dij()
{
	q.push(mp(0,1,0));
	memset(d,0x3f,sizeof(d));
	d[1][0]=0;
	int i,j,s,x,y,z;
	while(!q.empty())
	{
		s=q.top().first.first;
		x=q.top().first.second;
		y=q.top().second;
		q.pop();
		if(x==n) return s;
		if(vis[x][y]) continue;
		vis[x][y]=true;
		y|=a[x];
		for(i=pre[x];i;i=nxt[i])
		{
			j=to[i];
			if((w[i]|y)!=y) continue;
			if(d[j][y]>s+t[i])
			{
				d[j][y]=s+t[i];
				q.push(mp(d[j][y],j,y));
			}
		}
	}
	return -1;
}
int main()
{
	int m,p,k;
	scanf("%d%d%d%d",&n,&m,&p,&k);
	int i,j,x,y,l,z,o,u;
	for(i=1;i<=k;i++)
	{
		scanf("%d%d",&x,&y);
		for(j=1;j<=y;j++)
		{
			scanf("%d",&z);
			a[x]|=(1<<(z-1));
		}
	}
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d%d",&x,&y,&z,&o);
		u=0;
		for(j=1;j<=o;j++)
		{
			scanf("%d",&l);
			u|=(1<<(l-1));
		}
		ae(x,y,z,u);ae(y,x,z,u);
	}
	printf("%d\n",dij());
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值