Noip模拟赛小奇回地球

题面

题目描述
【题目背景】
开学了,小奇在回地球的路上,遇到了一个棘手的问题。
【问题描述】
简单来说,它要从标号为1的星球到标号为n的星球,某一些星球之间有航线。由于超时空隧道的存在,从一个星球到另一个星球时间可能会倒流,而且,从星球a到b耗费的时间和星球b到a耗费的时间不一定相同。
宇宙法规定:“禁止在出发时间前到达目的地。”
每艘飞船上都有速度调节装置,可以调节飞行的时间。其功能可以使得整次航程中所有两星球间的飞行时间增加或减少相同的整数值。你的任务是帮助它调整速度调节器,找出一条最短时间到达目的地的路径。
【输入格式】
输入文件包含多组数据,第1个数为T,表示数据组数。
对于每组数据,输入第1行为两个正整数n,m,为星球的个数和星球间的路线数。接下来m行,每行三个整数i,j和t,表示由星球i到星球j飞行的时间为t。由i到j最多只会有一条飞行线路。
【输出格式】
输出文件共T行,每组数据输出一行。
如果可以通过调节速度调节器完成任务,则输出一个非负整数,表示由星球1到星球n的最短时间。(注意最短时间要大于或者等于0)。
如果不能由星球1到达星球n,则输出-1。
【样例输入】
1
4 5
1 2 1
1 3 1
2 3 -3
3 1 1
3 4 1
【样例输出】
2
【样例解释】
把速度控制器的值设为1,相当于每个时间值加1,得到的最短路径为1→2→3→4,所需时间为2+(-2)+2=2。
【数据范围】
1,2号测试点,保证所有星球出度不超过1
3,4号测试点,n<=10
5,6号测试点,-100<=t<=100
对于100%的数据T<=10,n<=100,m<=n*(n-1),-100000<=t<=100000
数据随机和构造结合生成

废话

其实看到题面我就知道是hzwer这位神犇出的题,小奇?这不甜甜私房猫嘛。
我觉得我是第一个在教练讲之前就想到二分以及写出代码的垃圾,但是我wa掉了;

思路:

直接二分距离这个很容易想到,然后spfa判负环check一下,更新l,r
但是需要用floyd(或反向建边跑)判断点是否与n联通

总结:

放出错误代码

#include<bits/stdc++.h>
#define inf 1<<30
using namespace std;
const int maxn=100+10,maxm=10000+10;
int T;
int size=0;
bool flag=0;
int n,m;
int head[maxn],dis[maxn],inq[maxn],cnt[maxn];
int l,r;
struct edge
{
	int to,next,val;
}e[maxm];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
inline void init()
{
	size=0;
	for(int i=1;i<=maxn-1;i++)
	head[i]=0,dis[i]=inf,inq[maxn]=0;
	memset(e,sizeof(e),0);
}
inline void inita()
{
	for(int i=1;i<=maxn-1;i++)
	dis[i]=inf,cnt[i]=0,inq[i]=0;
}
inline void dfs(int u,int fa)
{
	if(flag==1)return;
	if(u==n){flag=1;return;}
	for(int i=head[u];i;i=e[i].next)
	{
		int to=e[i].to;
		if(to==fa)continue;
		dfs(to,u);
	}
}
inline bool spfa(int s,int d)
{
	queue<int>q;
	inita();
	dis[s]=0;
	cnt[s]=1;
	q.push(s);
	inq[s]=1;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		inq[u]=0;
		for(int i=head[u];i;i=e[i].next)
		{
			int to=e[i].to;
			if(dis[to]>dis[u]+e[i].val+d)
			{
				dis[to]=dis[u]+e[i].val+d;
				cnt[to]=cnt[u]+1;
				if(cnt[to]>n){return true;}
				if(!inq[to])
				{
					q.push(to);
					inq[to]=1;
				}
			}
		}
	}
	if(dis[n]<0||dis[n]==inf)
	return true;
	return false;
}
inline void addedge(int u,int v,int w)
{
    e[++size].to=v;
    e[size].val=w;
    e[size].next=head[u];
    head[u]=size;
}
int main()
{
    T=read();
    while(T--)
    {
    	n=read(),m=read();
    	init();
    	for(int i=1;i<=m;i++)
    	{
    		int u=read(),v=read(),w=read();
    		addedge(u,v,w);
		}
    	dfs(1,0);
    	if(flag==0)
		{
		printf("-1\n");
		continue;
	    }
	    else
	    {
	      l=-100000;r=100000;
		  while(l<=r)
		  {
			int m=(l+r)>>1;
			if(spfa(1,m))l=m+1;
			else r=m-1;
		  }
		  spfa(1,l);
		  printf("%d\n",dis[n]);
		}
	}
	return 0;
}

0分原因:
初始化出锅了

	for(int i=1;i<=maxn-1;i++)
	inq[maxn]=0;//令人窒息的操作,让人不禁虎躯一震,数组越界居然没报错?

但是改了也只有50分
50原因:
更新的点并不与n联通
比如下面这组数据:

1
4 4
1 4 5
1 2 1
2 3 -100
3 2 -100

90原因:
取边的最大最小值来优化二分距离,但被毒瘤数据卡掉Orz,其实如果出题人想卡,每组数据一个,你就退役了,所以细节需要注意

1
1
1 0

后来想了一下,其实二分这种算法,优不优化边界影响不会很大?毕竟O(logn)
比如我不优化100000,100000的边界,我优化后50000,100000
难道不是一次操作的事情吗?那还优化个屁啊
放上100分代码:

#include<bits/stdc++.h>
#define inf 1<<30
using namespace std;
const int maxn=100+10,maxm=10000+10;
int T;
int size=0;
bool flag=0;
int n,m;
int head[maxn],dis[maxn],inq[maxn],cnt[maxn];
int vis[maxn][maxn];
int l,r;
int ans;
struct edge
{
	int to,next,val;
}e[maxm];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
inline void init()
{
	size=0;
	for(int i=1;i<=maxn-1;i++)
	head[i]=0,inq[i]=0,cnt[i]=0;
	memset(e,sizeof(e),0);
	memset(vis,sizeof(vis),0);
}
inline void inita()
{
	for(int i=1;i<=maxn-1;i++)
	dis[i]=inf,inq[i]=0,cnt[i]=0;
}
inline void floyd()
{
	for(int i=1;i<=n;i++)vis[i][i]=1;
    for(int k=1;k<=n;k++)
	  for(int i=1;i<=n;i++)
	    for(int j=1;j<=n;j++)
	    vis[i][j]=vis[i][j]|(vis[i][k]&vis[k][j]);
	    
}
inline bool spfa(int s,int d)
{
	queue<int>q;
	inita();
	dis[s]=0;
	cnt[s]=1;
	q.push(s);
	inq[s]=1;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		inq[u]=0;
		for(int i=head[u];i;i=e[i].next)
		{
			int to=e[i].to;
			if(dis[to]>dis[u]+e[i].val+d&&vis[to][n])
			{
				dis[to]=dis[u]+e[i].val+d;
				cnt[to]=cnt[u]+1;
				if(cnt[to]>n){return true;}
				if(!inq[to])
				{
					q.push(to);
					inq[to]=1;
				}
			}
		}
	}
	if(dis[n]<0||dis[n]==inf)return true;
	return false ;
}
inline void addedge(int u,int v,int w)
{
    e[++size].to=v;
    e[size].val=w;
    e[size].next=head[u];
    head[u]=size;
}
int main()
{
    T=read();
    while(T--)
    {
    	n=read(),m=read();
    	init();
    	for(int i=1;i<=m;i++)
    	{
    		int u=read(),v=read(),w=read();
    		addedge(u,v,w);
    		vis[u][v]=1;
		}
	    floyd();
	    if(!vis[1][n])
	    {
	    	printf("-1\n");
	    	continue;
		}
		l=-1000000,r=1000000;
		ans=-1;
		while(l<=r)
		{
		    int m=(l+r)>>1;
			if(spfa(1,m))l=m+1;
			else r=m-1,ans=dis[n];
		}
		printf("%d\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值