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} \]
對於這樣的題我們把他轉化為一個圖來解決
具體實現是:
- 對於一個不等關系,把它轉化為形如\(x-y \le c\)的式子
- 由\(y\)向\(x\)連一條權為c的邊
- 若是‘=’ 號,轉化為\(x-y \le c\)和\(y-x \le c\),來處理
- 構建一個\(0\)節點,對每個節點的邊權都為\(0\),防止圖不連通,而且這樣不會影響最短路
- 跑一次最短路,因為有負權用spfa
大體的步驟,就是這些了
重點
但是,這道題顯得奇奇怪怪,為啥呢,用朴素的SPFA跑出TLE了,看題解都是用的dfs優化,可我偏不,事實上是不想刪代碼,我加了容錯SLF和mcfx,就輕松過掉了,時間優化巨大
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\)被卡了