ACM差分約束筆記
https://www.cnblogs.com/31415926535x/p/10463112.html
很早之前學最短路的時候就看了一眼差分約束,,當時以為這種問題不怎麼會出現,,而且當時為了只為了學最短路,,所以就沒有怎麼做題,,知道是什麼,但是不會建圖使用,,
然後上一次做cf就碰到了,,雖然那道題不只是差分約束能解決還卡時間,,但是萬一以後還出現這種題,,只是知道是這個型別的題卻不知道如何下手也相當於是不會啊,,所以抽時間重新看了看這塊的內容,,做幾道題,,順便背一背最短路的板子,,好久敲最短路的板子都已經忘記了,,
感覺這一塊的東西最主要的是建圖吧,,很多這樣的題的解法都不止一種,,差分約束只是其中一種,,因為使用spfa實現的,,所以也很容易被卡,,
概念
因為之前看過差分約束,,還有印象,,所以上手很快,,純理論性東西演算法導論等等的地方講的很詳細,,
首先差分約束主要是解決不等式組的求解 ,,其中這些不等式組的特徵是\(x_i-x_j \leq or \geq K_i(i,j \in [1, n], k \in [1, m])\) ,,
- 求\(x_n-x_0\) 的最大值就是求\(x_n\) 到\(x_0\) 的最短路,\(x_i-x_j \leq K_i\)
- 求\(x_n-x_0\) 的最小值就是求\(x_n\) 到\(x_0\) 的最長路,\(x_i-x_j \geq K_i\)
建圖都是建\(x_j\) ->\(x_i\) 的邊,權值為K
有些題目還有一些隱藏的條件,,比如說\(x_i-x_{i-1} \leq K_i\) 等等的約束條件,,一併加上就行了,
要是出現符號不一致的就兩邊取相反數,,把符號化一致就行,,(這樣會出現負權的邊,,所以要用spfa來解,,),,
出現\(x_i-x_j < K\) 的話可以化成\(x_i-x_j \leq K + 1\) 的形式(都是整數的情況下),,
判斷有無解的話就判斷建的圖有無環就行了,,,
例題
poj-1201-Intervals
題意
題意大概就是,給你n個區間\([l_i,r_i]\) 要求這些區間內必須要幾個數\(C_i\) ,問你滿足這些區間的最少的數,,,
看評論區裡很多人都是貪心+線段樹(樹狀陣列)做的,,
用差分約束的話就是將題目所給的東西轉化成若干個不等式,,然後明白要求什麼,,找出隱藏的條件,建圖求解,,
這道題我們用\(dis[i]\) 表示0~i這個區間至少要選幾個數(類似字首和的思想),,,然後任意一個區間就可以表示為\(dis[r]-dis[l - 1] \geq c_i\) ,,題目的隱藏條件是相鄰兩點直接的個數是0或1,,也就是\(0 \leq dis[i]-dis[i-1] \leq 1\) ,因為對於0這個點出這樣無法表示(dis[-1]),,所以對每一個點加一(向右偏移一個位置),,,最後求最長路就行了,,,
程式碼
//#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstdlib> #include <string.h> #include <algorithm> #include <queue> #define aaa cout<<233<<endl; #define endl '\n' #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f;//1061109567 const ll linf = 0x3f3f3f3f3f3f3f; const double eps = 1e-6; const double pi = 3.14159265358979; const int maxn = 1e5 + 5; const int maxm = 2e5 + 5; const ll mod = 1e9 + 7; struct edge { int v; int cost; edge(int _v = 0, int _cost = 0):v(_v), cost(_cost){} }; vector<edge> e[maxn]; void addedge(int u, int v, int w) { e[u].pb(edge(v, w)); } bool vis[maxn]; int cnt[maxn]; int dis[maxn]; bool spfa(int s, int n) { memset(vis, false, sizeof vis); memset(cnt, 0, sizeof cnt); cnt[s] = 1; for(int i = 1; i <= n; ++i)dis[i] = -inf; vis[s] = true; dis[s] = 0; queue<int> q; while(!q.empty())q.pop(); q.push(s); while(!q.empty()) { int u = q.front();q.pop(); vis[u] = false; for(int i = 0; i < e[u].size(); ++i) { int v = e[u][i].v; if(dis[v] < dis[u] + e[u][i].cost) { dis[v] = dis[u] + e[u][i].cost; if(!vis[v]) { vis[v] = true; q.push(v); if(++cnt[v] > n)return false; } } } } return true; } int main() { //freopen("233.in" , "r" , stdin); //freopen("233.out" , "w" , stdout); //ios_base::sync_with_stdio(0); //cin.tie(0);cout.tie(0); int n;scanf("%d", &n); int mi = inf, mx = 0, u, v, w; for(int i = 1; i <= n; ++i) { scanf("%d%d%d", &u, &v, &w); addedge(u, v + 1, w); mi = min(mi, u); mx = max(mx, v); } ++mx; for(int i = mi; i <= mx; ++i) { addedge(i, i + 1, 0); addedge(i + 1, i, -1); } spfa(mi, mx); printf("%d\n", dis[mx]); return 0; }
poj-1275-Cashier Employment
題意
題意是一天之內24個小時0點到23點,某個時間點需要的營業員的個數\(r[i]\) 給你,然後有一些應聘的人,他們開始工作的時間\(a[i]\) 給你,,每個人可以從開始的那個時間段工作8個小時,,然後問你最少應該聘用多少個人使得每個時間段的人數\(r[i]\) 是足夠的,,
分析
乍一看這題不知道怎麼下手,,就算是知道這是一道差分約束的題也不知道圖怎麼建,,
我的感覺是首先要 找出一個屬性使得它在不同兩個的狀態下的滿足的條件不同(也就是題目要求什麼,就找什麼關係(二項式),,也就是我們後面建圖時的點與點之間的關係,,而且是差的不等關係,,也就是構建出一個差分約束系統,,而這個屬性一般也就是我們要求的最值的一種最寬的情況,,(\(x_n\) 到\(x_0\) 的最值)
對於這道題來說,題目要我們求一天之內需要的最少的人數\(sum\) ,,也就是0點到23點的最小值,,這樣我們就能看出我們要列出一些時間段 內的約束條件,,用\(dis[i]\) 表示0點到i點這段時間內至少需要人數,,(又是字首和的思想),,,這樣一段時間內至少需要的人數就是\(dis[i] - dis[j] \leq K\) ,,
一個員工只能工作8個小時,所以我們可以得出:從i-8到i這段時間內工作的人數至少要大於i這個時間段內\(r[i]\) 所需的人數\(dis[i]-dis[i-8] \geq r[i]\) ,此時的\(i \geq 7\) ;
對於\(i \leq 7\) 的情況,我們可以推出\(sum-dis[i+16] + s[i] \geq r[i]\)
同時對於每一個小時內的最多的工作人數\(mp[i]\) 是確定的,,也就是說,\(0 \leq dis[i]-dis[i-1] \leq mp[i]\)
一整天的工作人數滿足:\(dis[24]-dis[0] \geq sum\)
上面一個不等式中有一個未知量sum,,它的取值是0~n,,可以二分列舉這個sum多次建圖求出最小的sum,,,
程式碼
//hdu //#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstdlib> #include <string.h> #include <algorithm> #include <queue> #include <map> #define aaa cout<<233<<endl; #define endl '\n' #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f;//1061109567 const ll linf = 0x3f3f3f3f3f3f3f; const double eps = 1e-6; const double pi = 3.14159265358979; const int maxn = 1e5 + 5; const int maxm = 2e5 + 5; const ll mod = 1e9 + 7; struct edge { int to, next, w; }edge[maxn]; int head[maxn], tot; void addedge(int u, int v, int w) { edge[tot].to = v; edge[tot].next = head[u]; edge[tot].w = w; head[u] = tot++; } void init() { tot = 0; memset(head, -1, sizeof head); } bool vis[maxn]; int dis[maxn], cnt[maxn]; bool spfa(int s, int n) { memset(vis, false, sizeof vis); memset(cnt, 0, sizeof cnt); for(int i = 0; i <= n; ++i)dis[i] = -inf; vis[s] = true; dis[s] = 0; cnt[s] = 1; queue<int> q; while(!q.empty())q.pop(); q.push(s); while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = false; //if(u == 24 && dis[u] > m)return 0; for(int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; int w = edge[i].w; if(dis[v] < dis[u] + w) { dis[v] = dis[u] + w; if(!vis[v]) { vis[v] = true; q.push(v); if(++cnt[v] > n)return false; } } } } return true; } int r[30], a[maxn]; map<int, int> mp; int check(int m) { init(); for(int i = 0; i <= 23; ++i) { addedge(i, i + 1, 0); addedge(i + 1, i, -mp[i]); } for(int i = 7; i <= 23; ++i) addedge(i - 8 + 1, i + 1, r[i]); for(int i = 0; i < 7; ++i) addedge(i + 16 + 1, i + 1, r[i] - m); addedge(0, 24, m); addedge(24, 0, -m); if(spfa(0, 30)) return dis[24]; else return 0; } int main() { //freopen("233.in" , "r" , stdin); //freopen("233.out" , "w" , stdout); //ios_base::sync_with_stdio(0); //cin.tie(0);cout.tie(0); int t;scanf("%d", &t); while(t--) { for(int i = 0; i <= 23; ++i)scanf("%d", &r[i]); int n;scanf("%d", &n); for(int i = 1; i <= n; ++i)scanf("%d", &a[i]); for(int i = 1; i <= n; ++i)++mp[a[i]]; int l = 0, r = n + 1; int ans = 0; //for(int i =1; i <= n; ++i)cout << check(i) << endl;return 0 ; while(l + 1 < r) { int m = (l + r) >> 1; int flag = check(m); //cout << l << r << m << flag << endl; if(m == flag) { r = m; ans = m; } else l = m; } if(l >= n) printf("No Solution\n"); else printf("%d\n", ans); } return 0; } //1 //1 0 3 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 //5 //0 //23 //22 //1 //10
hdu-3440-House Man
題意
題意大概是一個人可以在各個屋頂上跳,,但是必須要跳比現在的高的屋頂,,他可以不改變初始順序的情況下移動房子來改變他們的距離,,它最大的跳躍距離是d,,然後問你能不能從最矮的房子跳到最高的房子,,如果能,求出最大的這兩個房子間的距離
分析
首先是建圖,,我們用\(dis[i]\) 表示第1棟房子到第i棟房子之間的最大距離,,然後跑源點是最矮那棟房子的最短路就行了
對於每棟房子,,我們連一條矮房子i到較高房子j的邊表示\(dis[j]-dis[i] \leq d\) ,,注意這裡為了保證次序不變,,如果i的編號大於了j,,說明i棟房子在j的右邊,,這樣\(dis[i] \geq dis[j]\) ,,上面那個式子就是負的,,不成立(也就是無解),,所以要判斷一下,,,
還有一個隱藏條件: 相鄰兩棟房子之間的距離一定是\(dis[i+1] > dis[i]\) ,,也就是:\(dis[i] - dis[i+1] \leq -1\) ,,所以建邊(i+1)->i權值為-1
程式碼
沒嘗試過棧實現的spfa,,據說快一些,,大概是佇列時間的三分之一左右,,
普通的佇列實現
//hdu //#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstdlib> #include <string.h> #include <algorithm> #include <queue> #include <map> #define aaa cout<<233<<endl; #define endl '\n' #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f;//1061109567 const ll linf = 0x3f3f3f3f3f3f3f; const double eps = 1e-6; const double pi = 3.14159265358979; const int maxn = 1e5 + 5; const int maxm = 2e5 + 5; const ll mod = 1e9 + 7; struct edge { int to, next, w; }edge[maxn]; int head[maxn], tot; void init() { tot = 0; memset(head, -1, sizeof head); } void addedge(int u, int v, int w) { edge[tot].to = v; edge[tot].w = w; edge[tot].next = head[u]; head[u] = tot++; } bool vis[maxn]; int dis[maxn], cnt[maxn]; bool spfa(int s, int n) { for(int i = 0; i <= n; ++i)vis[i] = false; for(int i = 0; i <= n; ++i)cnt[i] = 0; for(int i = 0; i <= n; ++i)dis[i] = inf; vis[s] = true; cnt[s] = 1; dis[s] = 0; queue<int> q; while(!q.empty())q.pop(); q.push(s); while(!q.empty()) { int u = q.front();q.pop(); vis[u] = false; for(int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; int w = edge[i].w; if(dis[v] > dis[u] + w) { dis[v] = dis[u] + w; if(!vis[v]) { vis[v] = true; q.push(v); if(++cnt[v] > n)return false; } } } } return true; } struct node { int h, id; const bool operator<(const node &r)const { return h < r.h; } }node[maxn]; int main() { //freopen("233.in" , "r" , stdin); //freopen("233.out" , "w" , stdout); int t;scanf("%d", &t); for(int ca = 1; ca <= t; ++ca) { int n, d; scanf("%d%d", &n, &d); for(int i = 1; i <= n; ++i) { node[i].id = i; scanf("%d", &node[i].h); } sort(node + 1, node + 1 + n); init(); bool flag = true; for(int i = 1; i <= n - 1 && flag; ++i) { addedge(i + 1, i, -1); int u = min(node[i].id, node[i + 1].id); int v = max(node[i].id, node[i + 1].id); if(u > v)flag = false; addedge(u, v, d); } printf("Case %d: ", ca); int s = min(node[1].id, node[n].id); int t = max(node[1].id, node[n].id); if(!flag || !spfa(s, n))printf("-1\n"); elseprintf("%d\n", dis[t]); } return 0; }
佇列實現
//hdu //#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstdlib> #include <string.h> #include <algorithm> #include <queue> #include <map> #define aaa cout<<233<<endl; #define endl '\n' #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f;//1061109567 const ll linf = 0x3f3f3f3f3f3f3f; const double eps = 1e-6; const double pi = 3.14159265358979; const int maxn = 1e5 + 5; const int maxm = 2e5 + 5; const ll mod = 1e9 + 7; struct edge { int to, next, w; }edge[maxn]; int head[maxn], tot; void init() { tot = 0; memset(head, -1, sizeof head); } void addedge(int u, int v, int w) { edge[tot].to = v; edge[tot].w = w; edge[tot].next = head[u]; head[u] = tot++; } bool vis[maxn]; int dis[maxn], cnt[maxn]; bool spfa(int s, int n) { for(int i = 0; i <= n; ++i)vis[i] = false; for(int i = 0; i <= n; ++i)cnt[i] = 0; for(int i = 0; i <= n; ++i)dis[i] = inf; vis[s] = true; cnt[s] = 1; dis[s] = 0; queue<int> q; while(!q.empty())q.pop(); q.push(s); while(!q.empty()) { int u = q.front();q.pop(); vis[u] = false; for(int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; int w = edge[i].w; if(dis[v] > dis[u] + w) { dis[v] = dis[u] + w; if(!vis[v]) { vis[v] = true; q.push(v); if(++cnt[v] > n)return false; } } } } return true; } struct node { int h, id; const bool operator<(const node &r)const { return h < r.h; } }node[maxn]; int main() { //freopen("233.in" , "r" , stdin); //freopen("233.out" , "w" , stdout); int t;scanf("%d", &t); for(int ca = 1; ca <= t; ++ca) { int n, d; scanf("%d%d", &n, &d); for(int i = 1; i <= n; ++i) { node[i].id = i; scanf("%d", &node[i].h); } sort(node + 1, node + 1 + n); init(); bool flag = true; for(int i = 1; i <= n - 1 && flag; ++i) { addedge(i + 1, i, -1); int u = min(node[i].id, node[i + 1].id); int v = max(node[i].id, node[i + 1].id); if(u > v)flag = false; addedge(u, v, d); } printf("Case %d: ", ca); int s = min(node[1].id, node[n].id); int t = max(node[1].id, node[n].id); if(!flag || !spfa(s, n))printf("-1\n"); elseprintf("%d\n", dis[t]); } return 0; }
poj-3169-Layout
題意
一排牛,,有一些牛之間的距離不能超出d,有一些牛的距離不能小於d,,問你第一頭和最後一頭牛直接的距離的最大值是多少
分析
簡單的差分約束,,直接建圖就行了,,,(貌似不加相鄰兩頭之間距離大於1這個條件也能過)
圖有環為-1,,距離是inf為-2,其他的就是dis[n],,
程式碼
//hdu //#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstdlib> #include <string.h> #include <algorithm> #include <queue> #include <map> #define aaa cout<<233<<endl; #define endl '\n' #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f;//1061109567 const ll linf = 0x3f3f3f3f3f3f3f; const double eps = 1e-6; const double pi = 3.14159265358979; const int maxn = 1e6 + 5; const int maxm = 2e5 + 5; const ll mod = 1e9 + 7; struct edge { int to, next, w; }edge[maxn]; int head[maxn], tot; void init() { tot = 0; memset(head, -1, sizeof head); } void addedge(int u, int v, int w) { edge[tot].to = v; edge[tot].w = w; edge[tot].next = head[u]; head[u] = tot++; } bool vis[maxn]; int dis[maxn], cnt[maxn], sta[maxn]; int spfa(int s, int n) { for(int i = 1; i <= n; ++i)vis[i] = false; for(int i = 1; i <= n; ++i)dis[i] = inf; for(int i = 1; i <= n; ++i)cnt[i] = 0; vis[s] = true; cnt[s] = 1; dis[s] = 0; int top = -1; sta[++top] = s; while(~top) { int u = sta[top--]; vis[u] = false; for(int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; int w = edge[i].w; if(dis[v] > dis[u] + w) { dis[v] = dis[u] + w; if(!vis[v]) { vis[v] = true; sta[++top] = v; if(++cnt[v] > n)return -1; } } } } if(dis[n] == inf)return -2; return dis[n]; } int main() { //freopen("233.in" , "r" , stdin); //freopen("233.out" , "w" , stdout); //ios_base::sync_with_stdio(0); //cin.tie(0);cout.tie(0); int n, ml, md; scanf("%d%d%d", &n, &ml, &md); int u, v, w; init(); for(int i = 1; i <= ml; ++i) { scanf("%d%d%d", &u, &v, &w); if(u > v)swap(u, v); addedge(u, v, w); } for(int i = 1; i <= md; ++i) { scanf("%d%d%d", &u, &v, &w); if(u < v)swap(u, v); addedge(u, v, -w); } //for(int i = 1; i <= n; ++i) //addedge(i + 1, i, 0); printf("%d\n", spfa(1, n)); return 0; }
poj-1364-King
題意
題意是一個序列的一些子序列的和與k的大小關係給你,然後問你原序列的與一個數k的大小關係是否能確定出來,,
分析
還是字首和的思想,\(dis[i]\) 表示第一個數到第i個數的和,,那麼子序列[i,j]的和就表示為\(dis[j]-dis[i]\) ,,題目又給了一些子序列和與一個數的大小關係,也就是:\(dis[j] - dis[i] < or > K_i\) ,,用這個條件建圖,,因為最後的圖可能不連通,所以再加一個源點到所有點為0的邊,,
注意,題目給的是每個子序列的起點和它的長度,,大小關係沒有等於的情況,,加一減一就行了,,
程式碼
//hdu //#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstdlib> #include <string.h> #include <algorithm> #include <queue> #include <map> #define aaa cout<<233<<endl; #define endl '\n' #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f;//1061109567 const ll linf = 0x3f3f3f3f3f3f3f; const double eps = 1e-6; const double pi = 3.14159265358979; const int maxn = 1e6 + 5; const int maxm = 2e5 + 5; const ll mod = 1e9 + 7; struct edge { int to, w, next; }edge[maxn]; int head[maxn], tot; void init() { tot = 0; memset(head, -1, sizeof head); } void addedge(int u, int v, int w) { edge[tot].to = v; edge[tot].w = w; edge[tot].next = head[u]; head[u] = tot++; } bool vis[maxn]; int dis[maxn], cnt[maxn], sta[maxn]; bool spfa(int s, int n) { for(int i = 0; i <= n; ++i)vis[i] = false; for(int i = 0; i <= n; ++i)dis[i] = inf; for(int i = 0; i <= n; ++i)cnt[i] = 0; vis[s] = true; cnt[s] = 1; dis[s] = 0; int top = -1; sta[++top] = s; while(~top) { int u = sta[top--]; vis[u] = false; for(int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; int w = edge[i].w; if(dis[v] > dis[u] + w) { dis[v] = dis[u] + w; if(!vis[v]) { vis[v] = true; sta[++top] = v; if(++cnt[v] > n)return false; } } } } return true; } int main() { //freopen("233.in" , "r" , stdin); //freopen("233.out" , "w" , stdout); //ios_base::sync_with_stdio(0); //cin.tie(0);cout.tie(0); int n, m; while(~scanf("%d", &n) && n) { scanf("%d", &m); int u, v, d; char s[2]; init(); for(int i = 1; i <= m; ++i) { scanf("%d %d %s %d", &u, &v, s, &d); if(s[0] == 'g') addedge(u + v, u - 1, -d - 1); else addedge(u - 1, u + v, d - 1); } for(int i = 0; i <= n; ++i) addedge(n + 1, i, 0); if(spfa(n + 1, n + 1)) printf("lamentable kingdom\n"); else printf("successful conspiracy\n"); } return 0; }
poj-2983-Is the Information Reliable?
題意
n個站點排成一排,,給出一些描述資訊
兩個站點之間如果是P,,說明距離是確定的x
如果是V,,距離至少是1
問是否存在這樣一個序列滿足上面的條件
dis[i]表示第i站所在的位置距離第一個的距離,,這樣兩站的描述資訊就能化成很多的不等式來表示,,建圖判斷是否存在環就行了,,注意原圖可能不連通,所以加一個源點就行了,,,
按道理說棧實現spfa應該比佇列實現的快一些,,但是這道題用棧實現t了(不止我一個人),,emmm迷一遍的操作,,佇列可過,,
//hdu //#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstdlib> #include <string.h> #include <algorithm> #include <queue> #include <map> #define aaa cout<<233<<endl; #define endl '\n' #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f;//1061109567 const ll linf = 0x3f3f3f3f3f3f3f; const double eps = 1e-6; const double pi = 3.14159265358979; const int maxn = 1e6 + 5; const int maxm = 2e5 + 5; const ll mod = 1e9 + 7; struct edge { int to, next, w; }edge[maxn]; int head[maxn], tot; void init() { tot = 0; memset(head, -1, sizeof head); } void addedge(int u, int v, int w) { edge[tot].to = v; edge[tot].w = w; edge[tot].next = head[u]; head[u] = tot++; } bool vis[maxn]; int dis[maxn], cnt[maxn], sta[maxn]; bool spfa(int s, int n) { for(int i = 0; i <= n; ++i)vis[i] = false; for(int i = 0; i <= n; ++i)cnt[i] = 0; for(int i = 0; i <= n; ++i)dis[i] = -inf; vis[s] = true; cnt[s] = 1; dis[s] = 0; //int top = -1; //sta[++top] = s; queue<int> q; while(!q.empty())q.pop(); q.push(s); //while(~top) while(!q.empty()) { //int u = sta[top--]; int u = q.front(); q.pop(); vis[u] = false; for(int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; int w = edge[i].w; if(dis[v] < dis[u] + w) { dis[v] = dis[u] + w; if(!vis[v]) { vis[v] = true; //sta[++top] = v; q.push(v); if(++cnt[v] > n)return false; } } } } return true; } int main() { //freopen("233.in" , "r" , stdin); //freopen("233.out" , "w" , stdout); //ios_base::sync_with_stdio(0); //cin.tie(0);cout.tie(0); int n, m; while(~scanf("%d%d", &n, &m)) { init(); for(int i = 1; i <= m; ++i) { int u, v, w; char pv; w = 1; scanf(" %c %d %d", &pv, &u, &v); if(pv == 'P') { scanf("%d", &w); addedge(v, u, -w); } addedge(u, v, w); } for(int i = 1; i <= n; ++i) addedge(0, i, 0); if(spfa(0, n)) printf("Reliable\n"); else printf("Unreliable\n"); } return 0; }
codeofeces-1131d-D. Gourmet choice
做這些差分約束的題的主要的原因就是這道cf的題,,當時比賽的時候就有人說是差分約束的題,,但是因為我只是瞭解這塊內容,,但是實際的題目完全沒有寫過,,所以看到題也沒有什麼思路,,就放棄了,,
現在再看這道題,,感覺十分的簡單,,,
題意
大概的意思就是有n+m個點,,他們直接的大小關係已知(具體大或小多少沒有說),,,然後問你能不能給每一個點賦一個值使得滿足所給的關係,,
分析
一種解法是用並查集縮點後跑一邊拓撲排序,,最後求得的最長鏈就是答案,,,
用差分約束解的話就是用所給的關係直接建圖就行了,,對於i->j大於就正的建一條邊,小於就反著建一條邊,,等於就建兩條就行了,,,
因為圖可能是不連通的,,所以再弄個源點,連到每個點就行了,,,
因為最後要的是每一的節點一個數,,而且儘可能小,,所以就找出dis數組裡距離源點最小的那個數,,然後每一個點減去這個最小的數就是最後要賦的值了,,,
對了這題用鏈式前向星來建圖會T,,,換鄰接表就好了,,,(不是說鏈式前向星的效率更高嗎,,,emmmm,,迷,,,就像那道用棧的spfaT掉用佇列就過了一樣迷,,,
程式碼
//cf #include <bits/stdc++.h> //#include <iostream> //#include <cstdio> //#include <cstdlib> //#include <string.h> //#include <algorithm> #define aaa cout<<233<<endl; #define endl '\n' #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f;//1061109567 const ll linf = 0x3f3f3f3f3f3f3f; const double eps = 1e-6; const double pi = 3.14159265358979; const int maxn = 1e6 + 5; const int maxm = 2e5 + 5; const ll mod = 1e9 + 7; struct edge { int v, w; edge(int _v, int _w):v(_v), w(_w){} }; vector<edge> e[maxn]; void addedge(int u, int v, int w) { e[u].push_back(edge(v, w)); } bool vis[maxn]; int cnt[maxn], dis[maxn], sta[maxn]; bool spfa(int s, int n) { for(int i = 0; i <= n; ++i)vis[i] = false; for(int i = 0; i <= n; ++i)cnt[i] = 0; for(int i = 0; i <= n; ++i)dis[i] = inf; vis[s] = true; cnt[s] = 1; dis[s] = 0; int top = -1; sta[++top] = s; while(~top) { int u = sta[top--]; vis[u] = false; for(int i = 0; i < e[u].size(); ++i) { int v = e[u][i].v; int w = e[u][i].w; if(dis[v] > dis[u] + w) { dis[v] = dis[u] + w; if(!vis[v]) { vis[v] = true; sta[++top] = v; if(++cnt[v] > n)return false; } } } } return true; } char s[1005][1005]; int main() { //freopen("233.in" , "r" , stdin); //freopen("233.out" , "w" , stdout); //ios_base::sync_with_stdio(0); //cin.tie(0);cout.tie(0); int n, m; scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++i)scanf("%s", s[i] + 1); for(int i = 1; i <= n; ++i) { for(int j = 1; j <= m; ++j) { if(s[i][j] == '>') addedge(i, j + n, -1); else if(s[i][j] == '<') addedge(j + n, i, -1); else { addedge(i, j + n, 0); addedge(j + n, i, 0); } } } for(int i = 1; i <= n + m; ++i) addedge(0, i, 1); if(spfa(0, n + m)) { printf("Yes\n"); int k = *min_element(dis + 1, dis + 1 + n + m); for(int i = 1; i <= n; ++i) printf("%d ", dis[i] - k + 1); printf("\n"); for(int i = 1 + n; i <= n + m; ++i) printf("%d ", dis[i] - k + 1); printf("\n"); } else printf("No\n"); return 0; }
估計這一段時間裡是不會在做差分約束的題了,,,不過正好複習一遍最短路的寫法,,,
這貌似是寫的最長的一篇部落格了,,,30多K,,,,,233