Gym - 100765C King Berl VI 差分约束

这题和sgu298是一个题,但是在sgu上这个题是1.5s,在gym上是0.5s,就很坑。。。

题意可以看一下点击打开链接

查分约束,一般的做法是用spfa算法,但这题会TLE...各种乱改了好长时间,在vj上看了别人一个代码才明白

可以先用tarjan算法求强连通分量,如果一个点和他相邻的点在同一个强连通分量里,则无解

然后缩点,在用拓扑排序替代spfa求解

这样的复杂度只有O(e)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>

using namespace std;

const int maxn=10005;
const int maxm=300005;

int n,m;
int headz[maxn],headf[maxn];
int tot,ord,scc,top,idx;
int disz[maxn],disf[maxn];
int vis[maxn],inq[maxn];
int instack[maxn];
int DFN[maxn];
int low[maxn];
int st[maxn];
int block[maxn];
int head[maxn];
int que[maxn],que2[maxn];
int indeg[maxn],outdeg[maxn];

struct node
{
	int v,next,dis;
}edgez[maxm],edgef[maxm],edge[maxm];

void addedge(int u,int v,int d)
{
	edgez[tot].v=v;
	edgez[tot].dis=d;
	edgez[tot].next=headz[u];
	headz[u]=tot;
	edgef[tot].v=u;
	edgef[tot].dis=d;
	edgef[tot].next=headf[v];
	headf[v]=tot++;
}

void add(int u,int v,int d)
{
	edge[idx].v=v;
	edge[idx].dis=d;
	edge[idx].next=head[u];
	head[u]=idx++;
}

void init()
{
	memset(headz,-1,sizeof(headz));
	memset(headf,-1,sizeof(headf));
	memset(head,-1,sizeof(head));
	tot=0;
	ord=0;
	scc=0;
	top=0;
	idx=0;
}

void tarjan(int u)
{
	DFN[u]=low[u]=++ord;
	instack[st[++top]=u]=1;
	for(int i=head[u];~i;i=edge[i].next)
	{
		int v=edge[i].v;
		if(!DFN[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(instack[v])
			low[u]=min(low[u],DFN[v]);
	}
	int v;
	if(DFN[u]==low[u])
	{
		scc++;
		do
		{
			instack[v=st[top--]]=0;
			block[v]=scc; 
		}
		while(v!=u);
	}
}


int check()
{
	int u,v,d;
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++)
	{
		scanf("%d%d%d",&u,&v,&d);
		add(u,v,d);
	}
	for(int i=1;i<=n;i++)
	{
		if(!DFN[i])
			tarjan(i);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=head[i];j!=-1;j=edge[j].next)
		{
			int v=edge[j].v;
			if(block[i]==block[v]&&edge[j].dis)
				return 0;
			else if(block[i]!=block[v])
			{
				++indeg[block[v]];
				++outdeg[block[i]];
				addedge(block[i],block[v],edge[j].dis);
			}
		}
	}
	return 1;
}

int solve()
{
	int front=0,rear=0,front2=0,rear2=0;
	for(int i=1;i<=scc;i++)
	{
		disz[i]=10000;
		disf[i]=-10000;
		if(!indeg[i])
			que[++rear]=i;
		if(!outdeg[i])
			que2[++rear2]=i;
	}
	while(front!=rear)
	{
		int t=que[++front];
		for(int i=headz[t];i!=-1;i=edgez[i].next)
		{
			int v=edgez[i].v;
			--indeg[v];
			disz[v]=min(disz[v],disz[t]-edgez[i].dis);
			if(!indeg[v])
				que[++rear]=v;
		}
	}
	while(front2!=rear2)
	{
		int t=que2[++front2];
		for(int i=headf[t];i!=-1;i=edgef[i].next)
		{
			int v=edgef[i].v;
			--outdeg[v];
			disf[v]=max(disf[v],disf[t]+edgef[i].dis);
			if(!outdeg[v])
				que2[++rear2]=v;
		}
	}
	for(int i=1;i<=scc;i++)
	{
		if(disz[i]<=disf[i])
			return 0;
	}
	return 1;
}

void getans()
{
	disz[block[n]]=disf[block[n]];
	for(int i=1;i<=scc;i++)
	{
		for(int j=headz[que[i]];j!=-1;j=edgez[j].next)
		{
			disz[edgez[j].v]=min(disz[edgez[j].v],disz[que[i]]-edgez[j].dis);
		}
	}
	for(int i=1;i<n;i++)
		printf("%d ",disz[block[i]]);
	printf("%d\n",disz[block[n]]);
}
int main()
{
	init();
	if(!check())
		printf("-1\n");
	else if(!solve())
		printf("-1\n");
	else getans();
	return 0;
 } 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值