1. 程式人生 > >[AHOI2014/JSOI2014]支線劇情,洛谷P4043,線性規劃

[AHOI2014/JSOI2014]支線劇情,洛谷P4043,線性規劃

正題

      題目連結:[AHOI2014/JSOI2014]支線劇情

      這題看似很難,讓人很容易就想到有上下界的費用流。

      但是根本不用那麼麻煩。

      其實我們可以這樣看。

      對於每一條邊,經過的次數大於等於1.

      而對於每一個點,入度的經過次數總和大於等於出度的經過次數總和。

      我們就可以寫成m+n條約束。

      然後我們要使得每條邊經過次數乘單價最小。

      因為約束都是大於等於,而且求最小值。那麼我們就對偶一下。

      但是發現矩陣非常大。因為m=\sum k <=5000

      怎麼辦?

      發現對於每一條邊,x_i>=1,可以寫成x_i-1>=0,我們用一個新變數y。使得y_i=x_i-1

      那麼y_i>=0,這條約束是不用寫進矩陣裡面的,因為對於矩陣裡面的基變數和非基變數,我們都已經規定了他們的值是大於等於0的。

       接著,因為y_i=x_i-1,所以x_i=y_i+1,我們就把第二類約束中的xi全部換成yi。

       又因為對偶,所以現在每條約束的上限其實就是單價,單價大於0,滿足基可行解的形式。

       我們就可以直接求一便單純形就好了。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define INF 2147483647
using namespace std;

int n;
double a[5010][310];
int len=0;
double eps=1e-8;

void pivot(int x,int y){
	double temp=a[x][y];a[x][y]=1;
	for(int i=0;i<=n;i++) a[x][i]/=temp;
	for(int i=0;i<=len;i++) if(x!=i){
		temp=a[i][y];a[i][y]=0;
		for(int j=0;j<=n;j++) a[i][j]-=temp*a[x][j];
	}
}

void simplex(){
	double mmin;
	int x,y;
	while(true){
		x=y=0;
		for(int i=1;i<=n;i++) if(a[0][i]>eps && (y==0 || a[0][i]>a[0][y])) y=i;
		if(y==0) break;
		mmin=(double)1e15;
		for(int i=1;i<=len;i++) if(a[i][y]>eps && a[i][0]/a[i][y]<mmin) x=i,mmin=a[i][0]/a[i][y];
		if(x==0) break;
		pivot(x,y);	
	}
}

int main(){
	srand(2333);
	scanf("%d",&n);
	int k,b,c;
	double tot=0;
	for(int i=1;i<=n;i++){
		scanf("%d",&k);
		for(int j=1;j<=k;j++){
			scanf("%d %d",&b,&c);
			len++;if(i!=1) a[len][i]=-1,a[0][i]+=1;
			a[len][b]=1,a[0][b]-=1;a[len][0]=c;tot+=c;
		}
	}
	simplex();
	printf("%.0lf",-a[0][0]+tot);
}