ACM差分約束筆記

http://www.javashuo.com/article/p-vytoyzgd-e.htmlphp

很早以前學最短路的時候就看了一眼差分約束,,當時覺得這種問題不怎麼會出現,,並且當時爲了只爲了學最短路,,因此就沒有怎麼作題,,知道是什麼,可是不會建圖使用,,
而後上一次作cf就碰到了,,雖然那道題不僅是差分約束能解決還卡時間,,可是萬一之後還出現這種題,,只是知道是這個類型的題殊不知道如何下手也至關因而不會啊,,因此抽時間從新看了看這塊的內容,,作幾道題,,順便背一背最短路的板子,,很久敲最短路的板子都已經忘記了,,html

感受這一塊的東西最主要的是建圖吧,,不少這樣的題的解法都不止一種,,差分約束只是其中一種,,由於使用spfa實現的,,因此也很容易被卡,,node

概念

這裏的東西我是先參考這篇博客的還有這裏
由於以前看過差分約束,,還有印象,,因此上手很快,,純理論性東西算法導論等等的地方講的很詳細,,ios

首先差分約束主要是解決 不等式組的求解,,其中這些不等式組的特徵是 \(x_i-x_j \leq or \geq K_i(i,j \in [1, n], k \in [1, m])\),,c++

  • \(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來解,,),,dom

出現 \(x_i-x_j < K\) 的話能夠化成 \(x_i-x_j \leq K + 1\)的形式(都是整數的狀況下),,spa

判斷有無解的話就判斷建的圖有無環就好了,,,.net

額外的一些東西

例題

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,,,

參考1
參考2

代碼

//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");
        else                    printf("%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");
        else                    printf("%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

(end)
http://www.javashuo.com/article/p-vytoyzgd-e.html

相關文章
相關標籤/搜索