1. 程式人生 > >【BZOJ】1061: [Noi2008]誌願者招募

【BZOJ】1061: [Noi2008]誌願者招募

真的 inline LV ace 誌願者 algo bzoj div 誌願者招募

題解

可能是世界上最裸的一個單純形
(話說全幺模矩陣是啥我到現在都不知道)

假裝我們已經看過了算導,或者xxx的論文,知道了單純形是怎麽實現的
扔一個blog走掉。。https://www.cnblogs.com/ECJTUACM-873284962/p/7097864.html

那麽我們根據題意可以列出這樣的方程

\(x_i\)表示第\(i\)類誌願者招募的個數

根據題目可列線性規劃的式子
以樣例為例
\(z = min 2x_1 + 5x_2 + 2x_3\)
\(x_1 + 0 + 0 >= 2\)
\(x_1 + x_2 + 0 >= 3\)
\(0 + x_2 + x_3 >= 4\)


\(x_1,x_2,x_3 >= 0\)
顯然,標準型要求我們這些式子是小於號並且z要取max
好吧,反號?
不過我們有個很神奇的原理叫對偶原理
(我是真的不知道為啥……)

也就是
\(min c^T X\)
\(Ax = b\)
等價於
\(max b^T X\)
\(A^TX = c\)

好吧,這是我們喜歡的形式啊
然後我們的方程就可以寫成這個樣子

\(z = max 2x_1 + 3x_2 + 4x_3\)
\(x_1 + x_2 + 0 <= 2\)
\(0 + x_2 + x_3 <= 5\)
\(0 + 0 + x_3 <= 2\)

我們把這個方程轉換成松弛型(也就是全是等於號)
\(x_1 + x_2 + 0 + x_4 = 2\)


\(0 + x_2 + x_3 + x_5 = 5\)
\(0 + 0 + x_3 + x_6 = 2\)
我們3個方程組,6個元,是消不出來的,我們這是一些作為基變量,剩下的非基變量都設成0,那樣的話一定是單純形上的一個頂點

這是一個\(m * (n + m)\)的矩陣,有點大
我們在處理的時候,初始設定所有的\(x_4,x_5,x_6\)作為基變量

每一個方程就是一個關於基變量的等式,我們找到一個替入變量,找到能使替入變量值最大的方程組的等式,將替入變量的位置當做替出變量的位置,矩陣就是\(nm\)的了

代碼

#include <iostream>
#include <algorithm>
#include <cstdio> #include <cstring> #include <queue> #include <cmath> #define enter putchar(‘\n‘) #define mp make_pair #define pb push_back #define fi first #define se second #define eps 1e-7 //#define ivorysi using namespace std; typedef long long int64; typedef double db; template<class T> void read(T &res) { res = 0;char c = getchar();T f = 1; while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) f = -1; c = getchar(); } while(c >= ‘0‘ && c <= ‘9‘) { res = res * 10 + c - ‘0‘; c = getchar(); } res *= f; } template<class T> void out(T x) { if(x < 0) {putchar(‘-‘);x = -x;} if(x >= 10) { out(x / 10); } putchar(‘0‘ + x % 10); } const int MAXN = 1005; const int MAXM = 10005; int N,M; db b[MAXM],c[MAXN],a[MAXM][MAXN],ans; void pivot(int id,int p) { b[id] /= a[id][p]; for(int j = 1 ; j <= N ; ++j) { if(j != p) a[id][j] /= a[id][p]; } a[id][p] = 1 / a[id][p]; for(int i = 1 ; i <= M ; ++i) { if(i != id) { if(fabs(a[i][p]) < eps) continue; for(int j = 1 ; j <= N ; ++j) { if(j != p) a[i][j] -= a[i][p] * a[id][j]; } b[i] -= a[i][p] * b[id];a[i][p] = -a[i][p] * a[id][p]; } } for(int j = 1 ; j <= N ; ++j) { if(j != p) c[j] -= c[p] * a[id][j]; } ans += c[p] * b[id];c[p] = -c[p] * a[id][p]; } void Init() { read(N);read(M); for(int i = 1 ; i <= N ; ++i) scanf("%lf",&c[i]); int s,t; for(int i = 1 ; i <= M ; ++i) { scanf("%d %d %lf",&s,&t,&b[i]); for(int j = s ; j <= t ; ++j) { a[i][j] = 1.0; } } } void Solve() { while(1) { db t = -1;int p = 0; for(int i = 1 ; i <= N ; ++i) { if(c[i] > t) { t = c[i];p = i; } } if(t <= 0) {printf("%.0lf\n",ans);return;} t = 0x5fffffff;int id = 0; for(int j = 1 ;j <= M ; ++j) { if(a[j][p] > 0 && b[j] / a[j][p] < t) { t = b[j] / a[j][p]; id = j; } } pivot(id,p); } } int main() { #ifdef ivorysi freopen("f1.in","r",stdin); #endif Init(); Solve(); }

【BZOJ】1061: [Noi2008]誌願者招募