• 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;
    }
    
  • 相关阅读:
    C# 从Excel 批量导入数据库
    SQL、Linq和Lambda表达式 的关系
    layer 中的 layer.alert layer.msg layer.confirm
    jquery 关于使用 append 追加 元素后 事件无法触发
    eBay 开发流程
    WCF学习笔记(2)——使用IIS承载WCF服务
    WCF学习笔记(1)——Hello WCF
    [老老实实学WCF] 第十篇 消息通信模式(下) 双工
    [老老实实学WCF] 第九篇 消息通信模式(上) 请求应答与单向
    [老老实实学WCF] 第八篇 实例化
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/12578100.html
Copyright © 2020-2023  润新知