Luogu P1993 題解


p1993 小康的農場

CSP_S 1=之后就沒怎么寫題解。。

推薦博客食用

預備知識

明顯這是一道差分約束的題,以下簡稱差分

有些人可能不了解差分,請點 [傳送門]

至於用差分做的題的特征,無一都是你可以列出每個變量之間的一些不等關系,就如下

\[\begin{cases} x_1-x_2 \le a_1 \\ x_2-x_3 \le a_2 \\ x_3-x_4 \le a_3 \\ \ldots \ldots \\ x_n-x_n \le a_{n-1} \\ \end{cases} \]

對於這樣的題我們把他轉化為一個圖來解決

具體實現是:

  1. 對於一個不等關系,把它轉化為形如\(x-y \le c\)的式子
  2. \(y\)\(x\)連一條權為c的邊
  3. 若是‘=’ 號,轉化為\(x-y \le c\)\(y-x \le c\),來處理
  4. 構建一個\(0\)節點,對每個節點的邊權都為\(0\),防止圖不連通,而且這樣不會影響最短路
  5. 跑一次最短路,因為有負權用spfa

大體的步驟,就是這些了

重點

但是,這道題顯得奇奇怪怪,為啥呢,用朴素的SPFA跑出TLE了,看題解都是用的dfs優化,可我偏不,事實上是不想刪代碼,我加了容錯SLFmcfx,就輕松過掉了,時間優化巨大

SPFA死了嗎,沒有,因為就連p4779,也沒有卡住我

值得一提的是,你的容錯值定義的要靠近零一點,在\(-3\)\(0\),之間為能過掉,因為這個題有負權,如果全是正權的話在\(0\)\(4\) ,比較合適

還有一點就是,必須要有mcfx,區間的跨度在1000左右為適宜,為\(200\)\(1000\),最好,單獨的優化價值都不大,可是加在一起,就是巨大的飛躍

事實上,是因為我不會dfs的spfa,蒟蒻的無力。。。

哈哈

AC代碼

有詳細注釋

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int Maxn=1e4+11;
struct Node {
	int to,lac,wg;
}edge[Maxn*3];
int n,m,t,a,b,c,h[Maxn],cnt,dis[Maxn],tot[Maxn];
bool vis[Maxn];
void insert(int x,int y,int c){//鄰接表存邊 
	edge[cnt].lac=h[x];
	edge[cnt].to=y;
	edge[cnt].wg=c;
	h[x]=cnt++;//我的邊編號為從0開始,為了
	//找對應邊方便  ^1,即可 
}
int val=-1,l=102,r=1138;//玄學取值 
bool spfa(){
	memset(dis,63,sizeof dis);//剛開始給個最大值 
	dis[0]=0;vis[0]=1;tot[0]=1;
	deque<int> q;
	q.push_front(0);//0入隊 
	while(!q.empty()){
		int fr=*(q.begin()); 
		q.pop_front();
		vis[fr]=0;
		for(int i=h[fr];i!=-1;i=edge[i].lac){
			int to=edge[i].to;
			if(dis[to]>dis[fr]+edge[i].wg){//能更新則更新 
				dis[to]=dis[fr]+edge[i].wg;
				if(!vis[to]){//不在隊里考慮將他入隊 
					vis[to]=1;
					if(tot[to]>=l&&tot[to]<=r)	q.push_front(to);//mcfx優化 
					else if(q.size()==0) q.push_back(to);//隊里沒有東西,直接進隊 
					else if(dis[to]>dis[*(q.begin())]+val) q.push_back(to);//容錯SLF優化 
					else q.push_front(to);
					tot[to]++;//別忘記錄入隊次數 
					if(tot[to]==n) return false;//每個節點入隊達到n次就無解 
				}
			}
		}
	}
	return true;
}
int main() {
//	freopen("kk.in","r",stdin);//文件操作,因為我這種蒟蒻經常錯 
	scanf("%d%d",&n,&m);
	memset(h,-1,sizeof h);//初始化head,不能忘!
	for(int i=1;i<=n;i++){
		scanf("%d%d%d",&t,&a,&b);
		if(t==3){
			insert(a,b,0);
			insert(b,a,0);//對於等於的情況特殊處理 
			continue;
		}
		scanf("%d",&c);
		if(t==1) insert(a,b,-c);//尋常的連邊 
		if(t==2) insert(b,a,c);// 
	}
	for(int i=1;i<=n;i++) insert(0,i,0);//萬一不連通的話,每一個聯通的區域都有可能有負權圈 
	if(spfa()) printf("Yes");//bool型 spfa 
	else printf("No");
	return 0;
}

這樣子,小\(f\)再也 不用b擔心自己的\(spfa\)被卡了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM