[NOI2008] 志愿者招募
输入文件:employee.in
输出文件:employee.out
简单对比
时间限制:2 s
内存限制:512 MB
【问题描述】
申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要Ai 个人。
布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用是每人Ci
元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这并不是他的特长!于是布布找到了你,希望你帮他设计一种最
优的招募方案。
【输入格式】
输入文件的第一行包含两个整数N, M,表示完成项目的天数和可以招募的志愿者的种类。
接下来的一行中包含N 个非负整数,表示每天至少需要的志愿者人数。
接下来的M 行中每行包含三个整数Si, Ti, Ci,含义如上文所述。为了方便起见,我们可以认为每类志愿者的数量都是无限多的。
【输出格式】
输入文件中仅包含一个整数,表示你所设计的最优方案的总费用。
【输入样例】
3 3
2 3 4
1 2 2
2 3 5
3 3 2
【输出样例】
14
【样例说明】
招募3 名第一类志愿者和4 名第三类志愿者。
【数据规模和约定】
30%的数据中,1 ≤ N, M ≤ 10,1 ≤ Ai ≤ 10;
100%的数据中,1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均
不超过2^31-1。
https://www.byvoid.com/zhs/blog/noi-2008-employee
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <queue> 5 using namespace std; 6 const int INF=100000010; 7 const int maxn=11010; 8 const int maxm=100010; 9 int cnt=1,fir[maxn],to[maxm],nxt[maxm],cap[maxm],val[maxm]; 10 void addedge(int a,int b,int c,int v){ 11 nxt[++cnt]=fir[a];to[cnt]=b;cap[cnt]=c;val[cnt]=v;fir[a]=cnt; 12 } 13 int N,M,S,T; 14 int dis[maxn],path[maxn],tot[maxn],vis[maxn]; 15 16 queue<int>q; 17 int MCMF(){ 18 int ret=0; 19 while(true){ 20 memset(dis,127,sizeof(dis));dis[S]=0; 21 q.push(S);vis[S]=1; 22 while(!q.empty()){ 23 int node=q.front();q.pop();vis[node]=0; 24 for(int i=fir[node];i;i=nxt[i]) 25 if(cap[i]&&dis[to[i]]>dis[node]+val[i]){ 26 dis[to[i]]=dis[node]+val[i]; 27 path[to[i]]=i; 28 if(!vis[to[i]]){ 29 vis[to[i]]=1; 30 q.push(to[i]); 31 } 32 } 33 } 34 if(dis[T]==2139062143) 35 break; 36 37 int p=T,f=INF; 38 while(p!=S){ 39 f=min(f,cap[path[p]]); 40 p=to[path[p]^1]; 41 } 42 ret+=dis[T]*f;p=T; 43 while(p!=S){ 44 cap[path[p]]-=f; 45 cap[path[p]^1]+=f; 46 p=to[path[p]^1]; 47 } 48 } 49 return ret; 50 } 51 52 int main(){ 53 freopen("employee.in","r",stdin); 54 freopen("employee.out","w",stdout); 55 scanf("%d%d",&N,&M); 56 S=0;T=N+2; 57 for(int i=1;i<=N;i++) 58 scanf("%d",&tot[i]); 59 for(int i=1,a,b,c;i<=M;i++){ 60 scanf("%d%d%d",&a,&b,&c); 61 addedge(a,b+1,INF,c); 62 addedge(b+1,a,0,-c); 63 } 64 for(int i=1;i<=N+1;i++){ 65 int c=tot[i]-tot[i-1]; 66 if(c>0){ 67 addedge(S,i,c,0); 68 addedge(i,S,0,0); 69 } 70 if(c<0){ 71 addedge(i,T,-c,0); 72 addedge(T,i,0,0); 73 } 74 if(i>1){ 75 addedge(i,i-1,INF,0); 76 addedge(i-1,i,0,0); 77 } 78 } 79 printf("%d ",MCMF()); 80 return 0; 81 }
然后又用线性规划。
额,松弛型先天不足,数组开不了,无法通过,再学一下吧。
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <cmath> 5 using namespace std; 6 const int maxr=11010; 7 const int maxc=1010; 8 9 const double eps=1e-10; 10 int n,m,nxt[maxc]; 11 int N[maxr],B[maxr]; 12 double a[maxr][maxr],v; 13 double b[maxr],c[maxr]; 14 15 int SGN(double x){ 16 return (x>eps)-(x<-eps); 17 } 18 19 void Init(){ 20 B[0]=N[0]=0;v=0.0; 21 for(int i=1;i<=n;i++)N[++N[0]]=i; 22 for(int i=1;i<=m;i++)B[++B[0]]=i+n; 23 } 24 25 void Pivot(int l,int e){ 26 b[e]=b[l]/a[l][e]; 27 a[e][l]=1.0/a[l][e]; 28 for(int i=1;i<=N[0];i++) 29 if(N[i]!=e)a[e][N[i]]=a[l][N[i]]/a[l][e]; 30 31 int pre=0; 32 for(int i=1;i<=N[0];i++) 33 if(N[i]!=e&&SGN(a[e][N[i]])!=0) 34 {nxt[pre]=i;pre=i;} 35 nxt[pre]=0; 36 37 for(int i=1;i<=B[0];i++) 38 if(B[i]!=l){ 39 b[B[i]]-=a[B[i]][e]*b[e]; 40 a[B[i]][l]=-a[B[i]][e]*a[e][l]; 41 for(int j=nxt[0];j;j=nxt[j]) 42 if(N[j]!=e)a[B[i]][N[j]]-=a[B[i]][e]*a[e][N[j]]; 43 } 44 45 v+=c[e]*b[e]; 46 c[l]=-c[e]*a[e][l]; 47 for(int i=1;i<=N[0];i++) 48 if(N[i]!=e) 49 c[N[i]]-=c[e]*a[e][N[i]]; 50 for(int i=1;i<=N[0];i++)if(N[i]==e)N[i]=l; 51 for(int i=1;i<=B[0];i++)if(B[i]==l)B[i]=e; 52 } 53 54 void Simplex(){ 55 while(true){ 56 int e=maxr,l=maxr; 57 for(int i=1;i<=N[0];i++) 58 if(SGN(c[N[i]])>0&&e>N[i])e=N[i]; 59 60 if(e==maxr)break; 61 62 double lam=-1; 63 for(int i=1;i<=B[0];i++) 64 if(SGN(a[B[i]][e])>0){ 65 double tmp=b[B[i]]/a[B[i]][e]; 66 if(lam==-1||SGN(lam-tmp)>0||SGN(lam-tmp)==0&&l>B[i]) 67 {lam=tmp;l=B[i];} 68 } 69 70 Pivot(l,e); 71 } 72 } 73 74 int main(){ 75 #ifndef ONLINE_JUDGE 76 freopen("employee.in","r",stdin); 77 freopen("employee.out","w",stdout); 78 #endif 79 scanf("%d%d",&n,&m); 80 for(int i=1,t;i<=n;i++){ 81 scanf("%d",&t); 82 c[i]=t; 83 } 84 85 for(int i=1,l,r,t;i<=m;i++){ 86 scanf("%d%d%d",&l,&r,&t); 87 for(int j=l;j<=r;j++) 88 a[i+n][j]=1; 89 b[i+n]=t; 90 } 91 92 Init(); 93 Simplex(); 94 95 printf("%d ",(int)(v+0.5)); 96 return 0; 97 }
这个程序可以AC,但解法并不具有共性啊……
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int maxr=10010; 6 const int maxc=1010; 7 8 int n,m,nxt[maxc]; 9 int a[maxr][maxc]; 10 11 12 void Pivot(int l,int e){ 13 int pre=maxc-1; 14 for(int i=0;i<=n;i++) 15 if(a[l][i]!=0){nxt[pre]=i;pre=i;} 16 nxt[pre]=-1; 17 18 for(int i=0,t;i<=m;i++) 19 if(i!=l&&(t=a[i][e])){ 20 a[i][e]=0; 21 for(int j=nxt[maxc-1];j!=-1;j=nxt[j]) 22 a[i][j]+=t*a[l][j]; 23 } 24 } 25 26 void Simplex(){ 27 while(true){ 28 int e=0,l=0; 29 for(int i=1;i<=n;i++) 30 if(a[0][i]>0){e=i;break;} 31 if(e==0)break; 32 for(int i=1;i<=m;i++) 33 if(a[i][e]<0&&(!l||a[l][0]>a[i][0])) 34 {l=i;} 35 36 Pivot(l,e); 37 } 38 } 39 40 int main(){ 41 #ifndef ONLINE_JUDGE 42 freopen("employee.in","r",stdin); 43 freopen("employee.out","w",stdout); 44 #endif 45 scanf("%d%d",&n,&m); 46 for(int i=1;i<=n;i++) 47 scanf("%d",&a[0][i]); 48 for(int i=1,l,r,t;i<=m;i++){ 49 scanf("%d%d%d",&l,&r,&t); 50 for(int j=l;j<=r;j++) 51 a[i][j]=-1; 52 a[i][0]=t; 53 } 54 Simplex(); 55 printf("%d ",a[0][0]); 56 return 0; 57 }
这里的线性规划式子都是原式的对偶线性规划式。