3442: 学习小组
题目:传送门
题解:
超级好题啊大佬们的神题!建图肥肠灵性!感觉自己是星际玩家。。。
首先呢st直接向每个人连边,容量为min(k,喜欢的小组个数),费用为0
然后每个人再向ed连,因为题目要求人数尽量多,那么每个人都至少要去一个学习小组,那么容量就为min(k-1,喜欢的小组个数-1),费用为0(表示每个人最多能不选的小组)
每个人还要向自己喜欢的小组连边,容量为1,费用就为-F[i](因为题目问的是最小的支出,那么F表示的是手续费,所以肯定为负)
灵性的操作来了:
每个小组当然是还要向ed连的。
直接就把每个小组向ed连n条边,表示不同人数参加该小组时的花费
容量为1,流量就要推导一下:
对于有i-1个人参加了该小组j,那么多一个人的费用差就是:C[j]*i^2-C[j]*(i-1)^2
化简之后就是:C[j]*(2*i-1);
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 #define inf 999999999 7 using namespace std; 8 struct node 9 { 10 int x,y,c,d,next,other; 11 }a[510000];int len,last[210]; 12 void ins(int x,int y,int c,int d) 13 { 14 int k1,k2; 15 k1=++len; 16 a[len].x=x;a[len].y=y;a[len].c=c;a[len].d=d; 17 a[len].next=last[x];last[x]=len; 18 19 k2=++len; 20 a[len].x=y;a[len].y=x;a[len].c=0;a[len].d=-d; 21 a[len].next=last[y];last[y]=len; 22 23 a[k1].other=k2; 24 a[k2].other=k1; 25 } 26 int list[210],d[210],la[210],head,tail,st,ed,n,m,k,ans; 27 bool v[210]; 28 bool spfa() 29 { 30 for(int i=1;i<=ed;i++)d[i]=inf; 31 memset(v,false,sizeof(v));memset(la,0,sizeof(la)); 32 list[1]=st;d[st]=0;v[st]=true;head=1;tail=2; 33 while(head!=tail) 34 { 35 int x=list[head]; 36 for(int k=last[x];k;k=a[k].next) 37 { 38 int y=a[k].y; 39 if(a[k].c>0 && d[y]>d[x]+a[k].d) 40 { 41 d[y]=d[x]+a[k].d;la[y]=k; 42 if(v[y]==false) 43 { 44 v[y]=true; 45 list[tail++]=y; 46 if(tail==ed+1)tail=1; 47 } 48 } 49 } 50 head++;if(head==ed+1)head=1; 51 v[x]=false; 52 } 53 if(d[ed]==inf)return false; 54 return true; 55 } 56 int g_f() 57 { 58 int x,k,ans=0,f=inf; 59 x=ed;while(x!=st){k=la[x];f=min(f,a[k].c);x=a[k].x;} 60 x=ed;while(x!=st){k=la[x];a[k].c-=f;a[a[k].other].c+=f;x=a[k].x;} 61 ans+=d[ed]*f; 62 return ans; 63 } 64 int C[110],F[110],tot[110]; 65 char s[110]; 66 int main() 67 { 68 scanf("%d%d%d",&n,&m,&k);len=0;memset(last,0,sizeof(last));st=n+m+1,ed=st+1; 69 for(int i=1;i<=m;i++)scanf("%d",&C[i]);for(int i=1;i<=m;i++)scanf("%d",&F[i]); 70 for(int i=1;i<=n;i++) 71 { 72 scanf("%s",s+1); 73 for(int j=1;j<=m;j++)if(s[j]=='1')ins(i,j+n,1,-F[j]),tot[i]++; 74 } 75 for(int i=1;i<=n;i++)ins(st,i,min(k,tot[i]),0),ins(i,ed,min(k-1,tot[i]-1),0); 76 //C[j]*i^2-C[j]*(i-1)^2-->C[j]*(2*i-1) 77 for(int j=1;j<=m;j++)for(int i=1;i<=n;i++)ins(j+n,ed,1,C[j]*(2*i-1)); 78 ans=0;while(spfa())ans+=g_f();printf("%d ",ans); 79 return 0; 80 }