P1993 小K的农场

updata on 2020.3.31

今天做POJ 1364 king这题的时候发现,那种 spfa 的乱搞优化被卡掉了
所以这个题那种方法能过仅仅是因为数据水,以后还是不要用那种了
而且那个方法之前就没想明白有什么正确性


题目描述

小 K 在 MC 里面建立很多很多的农场,总共 (n) 个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共 (m) 个),以下列三种形式描述:

  • 农场 (a) 比农场 (b) 至少多种植了 (c) 个单位的作物;
  • 农场 (a) 比农场 (b) 至多多种植了 (c) 个单位的作物;
  • 农场 (a) 与农场 (b) 种植的作物数一样多。

但是,由于小 K 的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。

输入格式

第一行包括两个整数 (n)(m),分别表示农场数目和小 K 记忆中的信息数目。

接下来 (m) 行:

  • 如果每行的第一个数是 (1),接下来有三个整数 (a,b,c)表示农场 (a) 比农场 (b) 至少多种植了 (c) 个单位的作物;
  • 如果每行的第一个数是 (2),接下来有三个整数 (a,b,c)表示农场 (a) 比农场 (b) 至多多种植了 (c) 个单位的作物;
  • 如果每行的第一个数是 (3),接下来有两个整数 (a,b)表示农场 (a) 种植的的数量和 (b) 一样多。

输出格式

如果存在某种情况与小 K 的记忆吻合,输出 Yes,否则输出 No

数据规模与约定

对于 (100%) 的数据,保证 (1 le n,m,a,b,c le 10000)


差分约束
众所周知,差分约束可以解决形如(x_i-x_jleq y)的方程组
具体咋做,看这里

那么我们应该对这个题的三种情况做转化:

  1. (x_ageq x_b+cRightarrow x_b-x_aleq -c),对应的是加一条从(a ightarrow b)的边权为(-c)的边
  2. (x_aleq x_b+cRightarrow x_a-x_bleq c),对应加一条(b ightarrow a)的边权为(c)的边
  3. (x_a=x_bRightarrow x_a-x_bleq 0,x_b-x_aleq 0),也就是(a ightarrow b,b ightarrow a)分别一条(0)

然后别忘了建虚拟节点就行,差分约束传统技能

然而这个题把普通spfa卡了,开O2才能过
但是spfa最多也就(O(nm)),边就算最多也是(2m+n),算起来是(3cdot 10^8),2s应该能过,所以就很费解,应该是常数过大
然后改成Bellman-Ford想卡常数,然而更慢了
看题解里是dfs-spfa,但这种方法似乎并不稳定,而且考试的时候也没人告诉你这题卡普通spfa还是dfs-spfa...
所以最后用了一种玄学优化方法


但事实证明,这种方法还是被卡掉了,是这个题:POJ 1364 king
提交记录:被卡掉不加优化通过
所以一定少用这种优化,最好别用


所以可以有代码了

60-70分的普通spfa,开O2能AC,4.2s左右

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<queue>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	int x=0,y=1;
	char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
int n,m;
int fir[10006],nex[40006],to[40006],w[40006],tot;
int cnt[10005],in[10005];
int dis[10005];
std::queue<int>q;
inline void add(int a,int b,int c){
	to[++tot]=b;w[tot]=c;
	nex[tot]=fir[a];fir[a]=tot;
}
inline int spfa(){
	q.push(n+1);in[n+1]=1;
	std::memset(dis,0x3f,sizeof dis);dis[n+1]=0;
	while(!q.empty()){
		reg int u=q.front();q.pop();in[u]=0;
		for(reg int v,i=fir[u];i;i=nex[i]){
			v=to[i];
			if(dis[v]>dis[u]+w[i]){
				dis[v]=dis[u]+w[i];
				if(!in[v]){
					if(++cnt[v]==n) return 0;
					in[v]=1;q.push(v);
				}
			}
		}
	}
	return 1;
}
int main(){
	n=read();m=read();
	for(reg int a,b,c,i=1;i<=m;i++){
		c=read();a=read();b=read();
		if(c==1) c=read(),add(a,b,-c);
		else if(c==2) c=read(),add(b,a,c);
		else add(a,b,0),add(b,a,0);
	}
	for(reg int i=1;i<=n;i++) add(n+1,i,0);
	std::puts(spfa()?"Yes":"No");
	return 0;
}

经过玄学优化的spfa,用时2.4s,比上面的开O2快一倍
然而,那些dfs-spfa居然能不开O2,0ms,很是迷惑

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<utility>
#include<cstring>
#include<queue>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	int x=0,y=1;
	char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
int n,m;
int fir[10006],nex[40006],to[40006],w[40006],tot;
int cnt[10005],in[10005];
int dis[10005];
std::priority_queue<std::pair<int,int> >q;
inline int min(int a,int b){return a<b?a:b;}
inline void add(int a,int b,int c){
	to[++tot]=b;w[tot]=c;
	nex[tot]=fir[a];fir[a]=tot;
}
inline int spfa(){
	std::memset(dis,0x3f,sizeof dis);dis[n+1]=0;
	q.push(std::make_pair(0,n+1));in[n+1]=1;
	while(!q.empty()){
		reg int u=q.top().second;q.pop();in[u]=0;
		for(reg int v,i=fir[u];i;i=nex[i]){
			v=to[i];
			if(dis[v]>dis[u]+w[i]&&!in[v]){
				dis[v]=dis[u]+w[i];
				if(++cnt[v]>n) return 0;
				q.push(std::make_pair(-dis[v],v));in[v]=1;
			}
		}
	}
	return 1;
}
int main(){
	n=read();m=read();
	for(reg int a,b,c,i=1;i<=m;i++){
		c=read();a=read();b=read();
		if(c==1) c=read(),add(a,b,-c);
		else if(c==2) c=read(),add(b,a,c);
		else add(a,b,0),add(b,a,0);
	}
	for(reg int i=1;i<=n;i++) add(n+1,i,0);
	std::puts(spfa()?"Yes":"No");
	return 0;
}
原文地址:https://www.cnblogs.com/suxxsfe/p/12578100.html