bzoj3442: 学习小组(费用流好题)

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 }    
原文地址:https://www.cnblogs.com/CHerish_OI/p/8665766.html