java单纯形法_单纯形法 - fjzzq2002 - 博客园
看了集訓(xùn)隊(duì)答辯,感覺要學(xué)習(xí)的有杜教篩高級(jí)版、線性規(guī)劃、FFT、仙人掌、高級(jí)版線段樹
不出意外的話一個(gè)月內(nèi)博客內(nèi)都不會(huì)有別的東西了QAQ
首先是喜聞樂見的單純形法解線性規(guī)劃。
今年(2016年)和線性規(guī)劃有關(guān)的集訓(xùn)隊(duì)論文有兩篇,大家可以自行翻一下集訓(xùn)隊(duì)論文(當(dāng)然如果你沒有拿到你可以去UOJ群下載啊),下面的大部分內(nèi)容都是參閱akf那篇
線性規(guī)劃的標(biāo)準(zhǔn)型一般長得像這樣:
一般我們拿到的都是像標(biāo)準(zhǔn)型這樣的問題,例如網(wǎng)絡(luò)流問題,我們就是要最大化流量并且讓每個(gè)點(diǎn)流量守恒。
假設(shè)我們把匯點(diǎn)到源點(diǎn)連一條容量為+∞的邊那么所有點(diǎn)就都流量守恒了。我們用c(u,v)表示(u,v)這條邊的容量,f(u,v)表示這條邊的流量。
那么我們不難得到這樣的一個(gè)標(biāo)準(zhǔn)型線性規(guī)劃
不難把它轉(zhuǎn)化成標(biāo)準(zhǔn)型這樣的模型。
而最小費(fèi)用流則也是一個(gè)標(biāo)準(zhǔn)型,我們還是把匯點(diǎn)到源點(diǎn)加一條容量為+∞,費(fèi)用為0的邊。
那么也可以得到一個(gè)比較標(biāo)準(zhǔn)的線性規(guī)劃:
(較論文中的配文有修改)
如果你要最小費(fèi)用最大流的話只要加一個(gè)約束條件$\sum_{(u,v)}{f(u,v)}=maxflow$
額那不等號(hào)怎么辦呢?
對(duì)于不等約束條件${\sum_{j=1}^n{a_{ij}x_j}}\leq{b_i}$
在松弛型等式左邊的那些變量我們把它們叫做基變量,在上面的松弛型表示中就是x[n+1]…x[n+m],非基變量就是在等式右邊的那些,在上面的表示中是x[1]…x[n]。
顯然當(dāng)bi非負(fù)時(shí)令所有基變量為bi,非基變量為0,即可得到一個(gè)滿足條件的初始解。(至于bi可能為負(fù)的情況下面在單獨(dú)說)
單純形法有一個(gè)重要的操作,叫轉(zhuǎn)軸(pivot)操作。就是說我們可以把一個(gè)基變量x[b]和非基變量x[n]互換,用x[b]和其他非基變量代換這個(gè)x[n],這樣x[b]就成了非基變量,而x[n]成了基變量。
一開始我們知道。
那么簡(jiǎn)單代換一下會(huì)發(fā)現(xiàn),然后我們也可以把其他約束條件中的x[n]這樣代換掉,于是就完成了這個(gè)轉(zhuǎn)軸操作。
然后有了這個(gè)轉(zhuǎn)軸過程的幫助,我們就可以實(shí)現(xiàn)線性規(guī)劃算法了。
首先為了最大化目標(biāo)函數(shù),我們考慮不停地找一個(gè)系數(shù)為正的非基變量,然后增大這個(gè)x。
具體的這個(gè)最優(yōu)化過程如下:
(注意鄒逍遙原論文在這里的做法是選擇滿足的,ysy告訴我這樣是不科學(xué)的,akf的論文里寫的也是ce>0,于是做了一些修改)
這是為什么呢?akf在論文中給出了一個(gè)例子,從中我們可以大概理解一下。
這個(gè)線性規(guī)劃是這樣的:
我們考慮第一步我們選擇換正系數(shù)的x1,因?yàn)樵龃髕1必然會(huì)增大目標(biāo)函數(shù)。
我們分別考慮三個(gè)限制,顯然第一個(gè)限制下x1<=30,第二個(gè)限制下x1<=12,第三個(gè)限制x1<=9,然后x1<=9的下界最緊,所以我們用x1代換x6,得到下面的新線性規(guī)劃:
哇目標(biāo)函數(shù)增大到了27,可喜可賀。
接下來假設(shè)我們來增大x3,類似地可以得到:
然后可以增大x2,可以得到:
由于所有非基變量系數(shù)均為負(fù)的,所以我們不可能再得到更小的解了。
咦,有沒有注意到上面漏了什么東西說要下面講啊…
對(duì)了,在bi為負(fù)的時(shí)候,如果你把所有基變量帶0,而非基變量就不一定是一組合法的初始解。
這時(shí)候我們就需要一個(gè)初始化操作,初始化操作的基本思想是引入一個(gè)輔助線性規(guī)劃:
如果我們求得了這個(gè)輔助線性規(guī)劃的最優(yōu)解,那么如果最優(yōu)解中x0>0顯然原線性規(guī)劃無解。
如果最優(yōu)解中x0=0,那么x[1]…x[n+m]就是原線性規(guī)劃的一個(gè)可行解。
而我們?nèi)菀讟?gòu)造出輔助線性規(guī)劃的一個(gè)可行解。
我們只要把x0作為換入變量,找到bi的最小值bl,把x[i+l]用x0來替代。
例如:
我們引入輔助線性規(guī)劃:
bi最小值是-4,那么我們把x4用x0來替代:
然后我們就可以得到一組x的初始解用來進(jìn)行最優(yōu)化操作。
講道理:為什么bi這樣之后一定為正?
首先bi的最小值bl一定為負(fù)(廢話,否則就不用進(jìn)行最優(yōu)化操作了)
然后我們考慮第l個(gè)約束會(huì)變?yōu)?#xff1a;
而-bi顯然為正的。
而其他約束會(huì)變?yōu)?#xff1a;
由于bl是最小值,所以bi>=bl,所以-bl+bi>=0。
而我們發(fā)現(xiàn)UOJ上似乎有一種更加神奇的初始化方法?
我們的目標(biāo)顯然是讓所有系數(shù)bi都為非負(fù)的。
我們選擇一個(gè)bi為負(fù)的基變量x[i+n],然后我們?cè)谠摷s束右邊找一個(gè)系數(shù)為正的非基變量,然后進(jìn)行轉(zhuǎn)軸操作,如果沒有系數(shù)為正顯然就無解了。
額其實(shí)這和這個(gè)初始化操作本質(zhì)上是一樣的。
所以這樣就可以完成整個(gè)線性規(guī)劃的過程。
我們來分析一下時(shí)間復(fù)雜度?
pivot操作的復(fù)雜度顯然是O(NM)的,但是最優(yōu)化操作中pivot操作的調(diào)用次數(shù)可能會(huì)成為指數(shù)級(jí)。
但是我們可以發(fā)現(xiàn)要達(dá)到這個(gè)指數(shù)級(jí)的調(diào)用次數(shù),邊權(quán)也必須是指數(shù)級(jí)的。所以在OI中往往跑得比誰都快。所以“能在1s內(nèi)跑出范圍為幾百的數(shù)據(jù)”。
然而一般線性規(guī)劃是可以在多項(xiàng)式范圍內(nèi)求解的,只不過…
橢球算法 O(n^6*m^2)
內(nèi)點(diǎn)算法 O(n^3.5*m^2)
改進(jìn)的內(nèi)點(diǎn)算法 O(n^3.5*m)
在oi中一般并不能有什么卵用
所以單純形法就這么講完啦。
實(shí)現(xiàn)的時(shí)候可以發(fā)現(xiàn),寫單純形并不要真的把松弛型建出來,只要假裝第i個(gè)約束條件就對(duì)應(yīng)第i個(gè)基變量,把系數(shù)取負(fù)就可以了。
我的代碼(有一些細(xì)節(jié)寫的比較丑
#include #include#include#include#include#include#include#include#include#include
using namespacestd;
typedefdoubleld;#define SZ 233
intn,m,t,id[SZ];
ld a[SZ][SZ],vv[SZ];const ld eps=1e-7;intdcmp(ld x)
{if(xeps) return 1;return 0;
}void pivot(int r,int c) //基變量r和非基變量c
{
swap(id[r+n],id[c]);
ld x=-a[r][c]; a[r][c]=-1;for(int i=0;i<=n;i++) a[r][i]/=x;for(int i=0;i<=m;i++)
{if(dcmp(a[i][c])&&i!=r);else continue;
x=a[i][c]; a[i][c]=0;for(int j=0;j<=n;j++) a[i][j]+=x*a[r][j];
}
}voidsolve()
{for(int i=1;i<=n;i++) id[i]=i;while(1) //init
{int x=0,y=0;for(int i=1;i<=m;i++)
{if(dcmp(a[i][0])<0&&(!x||(rand()&1))) x=i;
}if(!x) break;for(int i=1;i<=n;i++)
{if(dcmp(a[x][i])>0&&(!y||(rand()&1))) {y=i; break;}
}if(!y) {puts("Infeasible"); return;}
pivot(x,y);
}while(1) //simplex
{int x=0,y=0;for(int i=1;i<=n;i++)
{if(dcmp(a[0][i])>0&&(!x||(rand()&1))) x=i;
}if(!x) break;double w,t; bool f=1;for(int i=1;i<=m;i++)
{if(dcmp(a[i][x])<0&&((t=-a[i][0]/a[i][x]),f||t
}if(!y) {puts("Unbounded"); return;}
pivot(y,x);
}
printf("%.9lf\n",a[0][0]);if(!t) return;for(int i=1;i<=n;i++) vv[i]=0;for(int i=n+1;i<=n+m;i++) vv[id[i]]=a[i-n][0];for(int i=1;i<=n;i++) printf("%.9lf",vv[i]);
}intmain()
{
scanf("%d%d%d",&n,&m,&t);for(int i=1;i<=n;i++) scanf("%lf",&a[0][i]);for(int i=1;i<=m;i++)
{for(int j=1;j<=n;j++) scanf("%lf",&a[i][j]), a[i][j]*=-1;
scanf("%lf",&a[i][0]);
}
solve();
}
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的java单纯形法_单纯形法 - fjzzq2002 - 博客园的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中类和对象_Python里的
- 下一篇: java 且_JAVA中逻辑运算符“|”