hdu 4284 Travel (2012 天津网络赛 1007 )

这题当场没有做出来, 看了芳姐代码后面还弄出了个超时尴尬,spfa 加位压缩的bfs过的;

先spfa算出目标点两两之间的最短距离;

bfs搜的是dp[i][j]; 当前所在点为i, 状态是j, 2进制j的第x位为1表示第x个目标点已经购买过了通行证;

复杂度O(15*(2^15))+O(15*15*spfa());险过


#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 113
#define maxm 11005
using namespace std;
int vv[maxm], nxt[maxm], ww[maxm], h[maxn], e;
int d[maxn], svis[maxn];

void link( int u, int v, int c )
{
	vv[e] = v; ww[e] = c; nxt[e] = h[u]; h[u] = e++;
}

void spfa( int s )
{
	memset( svis, 0, sizeof(svis) );
	memset( d, -1, sizeof(d) );
	d[s] = 0;
	queue<int>q;
	q.push( s );
	while( !q.empty() ){
		int u = q.front();
		q.pop();
		svis[u] = 0;
		for( int j = h[u]; j+1; j = nxt[j] ){
			int v = vv[j];
			if( d[v] == -1 || d[v] > d[u] + ww[j] ){
				d[v] = d[u] + ww[j];
				if( !svis[v] )
					svis[v] = 1, q.push( v );
			}
		}
	}
}

int C[20], D[20], dd[20][20];
int dp[20][70000], vis[20][70000];
int num[20], H, S;
int bit[20], n, m, money;

int bfs(int is)
{
	for( int i = 1; i <= H; i++ ){
		memset( dp[i], -1, sizeof(int)*(bit[H+1]+5) );
		memset( vis[i], 0, sizeof(int)*(bit[H+1]+5) );
	}
	//memset(dp, -1, sizeof(dp));
	//memset(vis, 0, sizeof(vis));
	queue<int> q;
	if(is){
		q.push(70000*S);
		dp[S][0] = money;
		vis[S][0] = 1;
	}
	else{
		for( int i = 1; i <= H; i++ ){
			if( dd[S][i] >= 0 && money >= dd[S][i] + D[i] ){
				dp[i][bit[i]] = money - dd[S][i] - D[i] + C[i];
				q.push( i * 70000 + bit[i] );
				vis[i][bit[i]] = 1;
			}
		}
	}
	while( !q.empty() ){
		int u = q.front(); q.pop();
		int st = u / 70000, state = u % 70000;
		vis[st][state] = 0;
		if( dp[st][state] < 0 ) continue;
		for( int i = 1; i <= H; i++ ){
			if( dp[st][state] - dd[st][i] - D[i] < 0 )
				continue;
			if( (state&bit[i]) == 0 && dd[st][i] >= 0 ){
				int stv = state | bit[i];
				if( dp[i][stv] < dp[st][state] - dd[st][i] - D[i] + C[i] ){
						dp[i][stv] = dp[st][state] - dd[st][i] - D[i] + C[i];
						int v = i*70000 + stv;
						if( !vis[i][stv] )
							vis[i][stv] = 1, q.push( v );
				}
			}
		}
	}
	for( int i = 1; i <= H; i++ )
		if( dd[i][S] >= 0 && dp[i][bit[H+1]-1] - dd[i][S] >= 0 )
			return 1;
	return 0;
}
int main()
{
	bit[1] = 1;
	for( int i = 2; i <= 18; i++ ) bit[i] = bit[i-1]<<1;
	int T;
	int a, b, c;
	scanf( "%d", &T );
	while( T-- ){
		scanf( "%d%d%d", &n, &m, &money );
		e = 0;
		memset( h, -1, sizeof(h) );
		for( int i = 0; i < m; i++ ){
			scanf( "%d%d%d", &a, &b, &c );
			link( a, b, c );
			link( b, a, c );
		}
		scanf( "%d", &H );
		S = 0;
		for( int i = 1; i <= H; i++ ){
			scanf( "%d%d%d", &num[i], &C[i], &D[i] );
			if( num[i] == 1 )
				S = i;
		}
		int s = S;
		if( !S ){
			num[++H] = 1;
			C[H] = 0; D[H] = 0; 
			S = H;
		}
		for( int i = 1; i <= H; i++ ){
			spfa( num[i] );
			for( int j = 1; j  <= H; j++ )
				dd[i][j] = d[num[j]];
		}
		if( !s ) H--;  // 这一步非常重要, 它把最大复杂度O(2^16)降为O(2^15),这个跑了3s, 乘2就超时了;
		if( bfs(s) )
			printf( "YES\n" );
		else
			printf( "NO\n" );
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值