1. 程式人生 > >bzoj2330糖果(差分約束)

bzoj2330糖果(差分約束)

題目描述
幼兒園裡有N個小朋友,lxhgww老師現在想要給這些小朋友們分配糖果,要求每個小朋友都要分到糖果。但是小朋友們也有嫉妒心,總是會提出一些要求,比如小明不希望小紅分到的糖果比他的多,於是在分配糖果的時候,lxhgww需要滿足小朋友們的K個要求。幼兒園的糖果總是有限的,lxhgww想知道他至少需要準備多少個糖果,才能使得每個小朋友都能夠分到糖果,並且滿足小朋友們所有的要求
輸入格式
輸入的第一行是兩個整數N,K。
接下來K行,表示這些點需要滿足的關係,每行3個數字,X,A,B。
如果X=1, 表示第A個小朋友分到的糖果必須和第B個小朋友分到的糖果一樣多;
如果X=2, 表示第A個小朋友分到的糖果必須少於第B個小朋友分到的糖果;
如果X=3, 表示第A個小朋友分到的糖果必須不少於第B個小朋友分到的糖果;
如果X=4, 表示第A個小朋友分到的糖果必須多於第B個小朋友分到的糖果;
如果X=5, 表示第A個小朋友分到的糖果必須不多於第B個小朋友分到的糖果
輸出格式
輸出一行,表示lxhgww老師至少需要準備的糖果數,如果不能滿足小朋友們的所有要求,就輸出-1

樣例輸入
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1

樣例輸出
11

分析:
分析:
裸題

最短路——差分約束

定義:
差分約束系統,是求解關於一組變數的特殊不等式組的方法
如果一個系統是由n個變數和m個約束條件組成,
其中每個約束條件形如xj-xi<=bk,則稱其為差分約束條件

解法
求解差分約束系統,
可以轉化為凸輪的單元最短路徑問題
觀察xj-xi<=bk
移項的xj<=xi+bk
很像最短路形式
這樣m條限制就轉化成了m條連線x,y且權值是b的邊(單向邊)
對這個圖執行spfa
最終的dis[i]即為一組可行解

注意

{dis[i]+x}都是合法解
存在負環則無合法解,存在不可到達的點,則存在無限解

spfa怎麼判斷負環呢
有兩種方法:
1)spfa的dfs形式,判斷條件是存在一點在一條路徑上出現多次
2)spfa的bfs形式,判斷條件是存在一點入隊次數大於總頂點數

tip

看了一些前輩的題解
發現這道題好像非常的毒
首先我們需要計算的是最小糖果數
所以要跑一個最大路

開3*n的邊
連邊:單向邊

不等式中<=號左邊的是way.x

建立一個超級源點0
0向n~1連邊(注意順序,不然會T)

注意in的維護

in[y]=in[r]+1、

開ll

在bzoj上就是WA,莫名其妙
辣雞爆炸oj

這裡寫程式碼片
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long

using namespace std;

const int N=100100;
int n,m;
struct node{
    int x,y,nxt,v;
};
node way[N*3];
int tot=0,st[N],in[N],q[N],tou,wei,dis[N];
bool p[N];

void add(int u,int w,int z)
{
    tot++;
    way[tot].x=u;way[tot].y=w;way[tot].v=z;way[tot].nxt=st[u];st[u]=tot;
}

void spfa()
{
    int i,j;
    bool ff=0;
    tou=wei=0;
    memset(p,1,sizeof(p));
    memset(dis,0xef,sizeof(dis));
    dis[0]=0;p[0]=0;
    q[++wei]=0;   //
    do
    {
        int r=q[++tou];
        for (int i=st[r];i;i=way[i].nxt)
        {
            int y=way[i].y;
            if (dis[y]<dis[r]+way[i].v)
            {
                dis[y]=dis[r]+way[i].v;
                in[y]=in[r]+1;   ///
                if (in[y]>n+1) {   //入隊次數過多 
                    printf("-1");
                    return;
                }
                if (p[y]){
                    p[y]=0; q[++wei]=y;
                }
            }
        }
        p[r]=1;
    }while (tou<wei);
    ll ans=0;
    for (int i=1;i<=n;i++) ans+=(ll)dis[i];
    printf("%lld",ans);
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        int opt,u,w;
        scanf("%d%d%d",&opt,&u,&w);
        if (opt==1){
            add(u,w,0); add(w,u,0);  //A>=B,B>=A
        }
        else if (opt==2){
            add(u,w,1);   //A<=B-1
        }
        else if (opt==3){
            add(w,u,0);  //B<=A
        }
        else if (opt==4){
            add(w,u,1);  //B<=A-1
        }
        else{
            add(u,w,0);   //A<=B
        }
    }
    for (int i=n;i;i--) add(0,i,1);  //SS
    spfa();
    return 0;
}