bzoj1061 [Noi2008]志愿者招募
Description
Input
Output
仅包含一个整数,表示你所设计的最优方案的总费用。
Sample Input
2 3 4
1 2 2
2 3 5
3 3 2
Sample Output
HINT
1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均 不超过2^31-1。
正解:不知道。。据说原本的正解线性规划被hack掉了。。
然而我连线性规划都不会,所以写了一个naive的费用流。。学了以后再来填坑吧。。填坑了,见下面的代码。。
考虑从原点向第0天连容量为inf,费用为0的边,第i天向第i+1天连容量为inf-a[i],费用为0的边,我们把这条线称之为主线。第n天向汇点连容量为inf,费用为0的边。对于每一段区间,从第s-1天到第t天连容量为inf,费用为c的边,我们把这条线称为副线。那么跑费用流时,主线上的堵塞流就自动地转移到副线上,同时,副线又因为有费用,那么我们就可以算出最小的费用了。
1 //It is made by wfj_2048~ 2 #include <algorithm> 3 #include <iostream> 4 #include <complex> 5 #include <cstring> 6 #include <cstdlib> 7 #include <cstdio> 8 #include <vector> 9 #include <cmath> 10 #include <queue> 11 #include <stack> 12 #include <map> 13 #include <set> 14 #define inf (1<<29) 15 #define il inline 16 #define RG register 17 #define ll long long 18 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 19 20 using namespace std; 21 22 struct edge{ int nt,to,flow,cap,cost; }g[30010]; 23 24 25 queue <int> Q; 26 int head[1010],dis[1010],flow[1010],fa[1010],p[1010],a[1010],n,m,S,T,num=1,cost; 27 28 il int gi(){ 29 RG int x=0,q=1; RG char ch=getchar(); while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 30 if (ch=='-') q=-1,ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x; 31 } 32 33 il void insert(RG int from,RG int to,RG int cap,RG int cost){ g[++num]=(edge){head[from],to,0,cap,cost},head[from]=num; } 34 35 il int bfs(RG int S,RG int T){ 36 for (RG int i=1;i<=n+3;++i) dis[i]=inf; 37 Q.push(S),flow[S]=inf,dis[S]=0; 38 while (!Q.empty()){ 39 RG int x=Q.front(),v; Q.pop(); 40 for (RG int i=head[x];i;i=g[i].nt){ 41 v=g[i].to; 42 if (dis[v]>dis[x]+g[i].cost && g[i].cap>g[i].flow){ 43 flow[v]=min(flow[x],g[i].cap-g[i].flow); 44 dis[v]=dis[x]+g[i].cost; fa[v]=x,p[v]=i,Q.push(v); 45 } 46 } 47 } 48 if (dis[T]==inf) return 0; cost+=flow[T]*dis[T]; 49 for (RG int x=T;x!=S;x=fa[x]) g[p[x]].flow+=flow[T],g[p[x]^1].flow-=flow[T]; 50 return 1; 51 } 52 53 il int mcmf(RG int S,RG int T){ cost=0; while (bfs(S,T)); return cost; } 54 55 il void work(){ 56 n=gi(),m=gi(); S=n+2,T=n+3; insert(S,1,inf,0),insert(1,S,0,0); 57 for (RG int i=1;i<=n;++i) a[i]=gi(),insert(i,i+1,inf-a[i],0),insert(i+1,i,0,0); 58 for (RG int i=1;i<=m;++i){ 59 RG int s=gi(),t=gi(),c=gi(); 60 insert(s,t+1,inf,c),insert(t+1,s,0,-c); 61 } 62 insert(n+1,T,inf,0),insert(T,n+1,0,0); 63 printf("%d\n",mcmf(S,T)); return; 64 } 65 66 int main(){ 67 File("volunteer"); 68 work(); 69 return 0; 70 }
这题用线性规划,设第i种志愿者为xi,则根据样例,必须满足:
min{2x1+5x2+2x3},x1>=2,x1+x2>=3,x2+x3>=4,x1,x2,x3>=0。
但是这不是线性规划的标准型,没有基本可行解,所以我们可以考虑对偶变换。
给定一个最大化目标的线性规划,我们应该描述如何形式化一个对偶线性规划,其中目标是最小化,而且最优值与初始线性规划的最优值相同。当表示对偶性规划时,我们称初始的线性规划为原始线性规划。为了构造对偶问题,我们将最大化改为最小化,交换右边系数与目标系数,并且将小于等于改为大于等于。原始问题的m个越是,每一个在对偶问题中都有一个对应的变量yi,对偶问题的n个约束,每一个在原始问题中都有一个对应的变量xj。
——算法导论
然后我们就可以把上面的线性规划变成:
max{2x1+3x2+4x3},x1+x2<=2,x2+x3<=5,x3<=2,x1,x2,x3>=0。
这样,我们就可以直接套用模板了。
1 //It is made by wfj_2048~ 2 #include <algorithm> 3 #include <iostream> 4 #include <complex> 5 #include <cstring> 6 #include <cstdlib> 7 #include <cstdio> 8 #include <vector> 9 #include <cmath> 10 #include <queue> 11 #include <stack> 12 #include <map> 13 #include <set> 14 #define eps (1e-12) 15 #define inf (1e15) 16 #define il inline 17 #define RG register 18 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 19 20 using namespace std; 21 22 double a[10010][1010],ans; 23 int b[1010],n,m; 24 25 il int gi(){ 26 RG int x=0,q=1; RG char ch=getchar(); while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 27 if (ch=='-') q=-1,ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x; 28 } 29 30 il void pivot(RG int l,RG int e){ 31 RG double k=a[l][e]; a[l][e]=1; 32 for (RG int i=0;i<=n;++i) a[l][i]/=k; RG int len=0; 33 for (RG int i=0;i<=n;++i) if (fabs(a[l][i])>eps) b[++len]=i; 34 for (RG int i=0;i<=m;++i) 35 if (i!=l && fabs(a[i][e])>eps){ 36 k=a[i][e],a[i][e]=0; 37 for (RG int j=1;j<=len;++j) a[i][b[j]]-=k*a[l][b[j]]; 38 } 39 return; 40 } 41 42 il double simplex(){ 43 while (1){ 44 RG int l,e; for (e=1;e<=n;++e) if (a[0][e]>eps) break; 45 if (e==n+1) return -a[0][0]; RG double tmp=inf; 46 for (RG int i=1;i<=m;++i) 47 if (a[i][e]>eps && a[i][0]/a[i][e]<tmp) tmp=a[i][0]/a[i][e],l=i; 48 if (tmp==inf) return inf; pivot(l,e); 49 } 50 } 51 52 il void work(){ 53 n=gi(),m=gi(); RG int s,t,d; 54 for (RG int i=1;i<=n;++i) scanf("%lf",&a[0][i]); 55 for (RG int i=1;i<=m;++i){ 56 s=gi(),t=gi(),d=gi(),a[i][0]=d; 57 for (RG int j=s;j<=t;++j) a[i][j]=1; 58 } 59 printf("%d",(int)(simplex()+0.5)); return; 60 } 61 62 int main(){ 63 File("volunteer+"); 64 work(); 65 return 0; 66 }