Light OJ 1074 Extended Traffic 解题报告

24 篇文章 0 订阅
11 篇文章 0 订阅

Question Link

题目大意

n个点,每个点都有一个拥挤度,m条边,由 u 到 v 点的代价是 v 的拥挤度 减去 u 的拥挤度 的差 的立方, 由题意分析可知,这是一个有向图,接下来q个数字,num i ,这个数字 numi 代表结点,问由结点 1 到 numi ,的情况。如果不能联通,或者,到达他的代价总和,小于 3 则,输出 ?

题目分析

由于是立方,所以我们知道这个图的边是有负权的,这就意味这,这个图可能有负环,在大量数据面前,绝对有负环回路,dijkstra,根据贪心思想来考虑的算法显然行不通。只有,bellman or SPFA , 类似于暴力枚举的方法。

关键来了

代价总和小于等于 3 如果代价总和为 负数 同样满足条件,那么意味着,能有负环回路到达的点,这个点的代价总和,绝对会是负数,走过负权回路的次数越多,代价越小,所以我们知道,在负权回路中的点,和 负权回路能到达的点,一定输出 ?。 但是,这是充分条件,而非必要条件。有不经过负权回路,但是dis的值是小于 3 的结点。所以 我们判断,在能由负环到达的点,dis 小于 3 的点, 无法到达的点,都输出 ? 。

接下来是一些细节(基于SPFA)
如何判断点能被负环达到?

c结点 入队次数超过 n 那么 , c结点能被负环达到,意味着在经过n个(n-1也行)结点后,还能被松弛,那么他一定能够被负环到达。这时候,如果,d结点这个结点c结点,到达,那么起点 1 到达 d 结点的代价总和,在历经很多次负环后,一定会是负数。

为什么已经判断为负环的点不能进入下次松弛?

因为入队次数 是 无限 , 会炸表。

细节添加

保存入队次数输出数组 intime
是否能被负环到达的数组 necir

AC code

#include<iostream>
#include<queue>
#include<cstring>
#include<string>
#include<sstream>
#include<map>
#include<vector>
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<stack>
#include<ctime>
using namespace std; 
#define rep(i,aa,bb) for(register int i=aa;i<=bb;i++)
#define rrep(i,aa,bb) for(register int i=aa;i>=bb;i--)
#define mset(var,val)	 memset(var,val,sizeof(var))
#define LL long long 
#define eps 0.000001
#define inf 0x3f3f3f3f
#define llinf 1e18
#define exp 0.000001
#define pai 3.141592654
#define random(x)   rand()%(x)
#define lowbit(x)   x&(-x)
inline int read()
{
	int x=0,y=1;char a=getchar();while ( a>'9' || a<'0'){if ( a=='-')y=-1;a=getchar();}
	while ( a>='0' && a<='9' ){	x=(x<<3)+(x<<1)+a-'0'; a=getchar();}return x*y;
}
#define N 220
int cro[N],n,m;bool inq[N],necir[N];int intime[N],dis[N]; 
struct Ead{
	int u,v,w,nx; 
}e[N*N];int hea[N],tot; 
void init(){
	mset(cro,0);
	mset(inq,0);
	mset(necir,0);
	mset(intime,0);
	mset(hea,0);
	mset(dis,0);
	tot = 0; 	
}
void add_ead(int u,int v,int w){
	++tot; 
	e[tot].u = u; e[tot].v = v; e[tot].w = w; e[tot].nx = hea[u]; hea[u] = tot; 
}
void dfs(int nod){
	necir[ nod ] = 1; 
	for (int i = hea[ nod ] ; i ; i = e[i].nx ){
		if ( !necir[ e[i].v ] )
			dfs( e[i].v );
	}
}
void SPFA(int st){
	rep(i,1,n)	dis[ i ] = inf ; 
	dis[ st ] = 0; 
	queue< int >q; 
	q.push(st);
	inq[ st ] = 1; 
	intime[ st ] = 1; 
	while ( !q.empty() ){
		int u = q.front(); q.pop();
		inq[ u ] = 0 ; 
		if ( necir[ u ] )		continue; 
		for (int i = hea[ u ] ; i ; i = e[i].nx ){
			if ( dis[ e[i].v ] > dis[ u ] + e[ i ].w && e[i].w != inf && dis[u] != inf ){
				dis[ e[i].v ] = dis[ u ] + e[ i ].w;
				if ( !inq[ e[i].v ] ) {
					inq[ e[i].v ] = 1; 
					intime[ e[i].v ]++;
					q.push( e[i].v );
					if ( intime[ e[i].v ] > n ){
						dfs( e[i].v );
					}
				}
			}
		}
	}
}
int main()
{
//	freopen("1.txt","r",stdin);
//	srand((int)time(0));
//	std::ios::sync_with_stdio(false);
	int T; T = read();
	rep(aaai,1,T){
		init();
		n = read(); 
		rep(i,1,n)	cro[i] = read();
		m = read();
		rep(i,1,m){
			int u,v,w; 
			u = read(); v = read(); w = (cro[v]-cro[u])*(cro[v]-cro[u])*(cro[v]-cro[u]);
			add_ead(u,v,w);
		}
		SPFA(1);
		int q = read();
		printf("Case %d:\n",aaai);
		rep(i,1,q){
			int tem = read();
			if ( necir[ tem ] || dis[ tem ] < 3 || dis[ tem ] == inf ){
				printf("?\n");
			}
			else printf("%d\n",dis[tem]);
		}
	} 
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值