#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43057197"); }
题意:那个要尽量多的学生参加的意思就是一个学生如果对至少一个小组有兴趣,就一定要让他参加至少一个小组,然后每个学生至多参加k个,但是不是强迫参加。
题解:费用流
算了,还是说说建图吧。
模式:add(入点,出点,流量,费用)(别忘了反向弧)
add(源点,学生,k,0),add(学生,汇点,k-1,0)
对于每个学生A有兴趣的小组B add(A,B+n,1,0)
连n条边,第j条为 add(小组,汇点,C(2j-1)-F)
有些思想写了太水浪费时间,不写怕初学者看不懂%
算了不写了,反正博客都是给自己看的。
看不懂的话留言吧。至少这几个月都是天天上blog的。
代码:
#include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 200 #define M 50000 #define inf 0x3f3f3f3f #define INF 0x3f3f3f3f3f3f3f3fll using namespace std; struct KSD { int u,v,len,fee,next; }e[M]; int head[N],cnt; inline void add(int u,int v,int len,int fee) { e[++cnt].u=u; e[cnt].v=v; e[cnt].len=len; e[cnt].fee=fee; e[cnt].next=head[u]; head[u]=cnt; } int dist[N],s,t; int lim[N],pre[N]; bool in[N]; queue<int>q; void spfa() { while(!q.empty())q.pop(); memset(dist,0x3f,sizeof dist); q.push(s),dist[s]=0,in[s]=1; int i,u,v; while(!q.empty()) { u=q.front(),q.pop(),in[u]=0; for(i=head[u];i;i=e[i].next)if(e[i].len) { if(dist[v=e[i].v]>dist[u]+e[i].fee) { dist[v]=dist[u]+e[i].fee; lim[v]=min(e[i].len,lim[u]); pre[v]=i; if(!in[v])q.push(v),in[v]=1; } } } return ; } void handle(int flow) { for(int i=pre[t];i;i=pre[e[i].u]) { e[i].len-=flow; e[i^1].len+=flow; } } int minfee,n,m,p; int C[N],F[N]; char str[N]; void build() { int i,j,k; scanf("%d%d%d",&n,&m,&p); s=n+m+1,t=n+m+2; cnt=1,lim[s]=inf; for(i=1;i<=m;i++)scanf("%d",&C[i]); for(i=1;i<=m;i++)scanf("%d",&F[i]); for(i=1;i<=n;i++) { add(s,i,p,0),add(i,s,0,0); // 每个学生可以选择小组的数量 if(p>1)add(i,t,p-1,0),add(t,i,0,0); // 每个学生可以放弃选择小组的次数 } for(i=1;i<=m;i++) { for(j=1;j<=n;j++) { int Fee=C[i]*(2*j-1)-F[i]; add(i+n,t,1,Fee),add(t,i+n,0,-Fee); } } for(i=1;i<=n;i++) { scanf("%s",str+1); for(j=1;j<=m;j++)if(str[j]=='1') { add(i,j+n,1,0); add(j+n,i,0,0); } } return ; } int main() { freopen("test.in","r",stdin); build(); while(spfa(),dist[t]<inf) { minfee+=lim[t]*dist[t]; handle(lim[t]); } cout<<minfee<<endl; return 0; }