拍照

题目描述

小B有n个下属,现小B要带着一些下属让别人拍照。

有m个人,每个人都愿意付给小B一定钱让n个人中的一些人进行合影。如果这一些人没带齐那么就不能拍照,小B也不会得到钱。

注意:带下属不是白带的!!!对于每个下属,如果他带了那么小B需要给他一些钱,保证当他拍照时配合。

请问,小B的净收益最多是多少。

输入输出格式

输入格式:

第1行有2个正整数m和n(0<m,n<=100)。接下来的m行,每行是一个要求拍照的人的有关数据。第一个数是他同意支付该合影的费用;接着是该合影需要的若干下属的编号,以一个0作为行的结束标记。最后一行的n个数是带每个下属的费用。

输出格式:

一个数,表示最大收益。小B可以一个人也不带。

输入输出样例

输入样例#1:
2 3
10 1 2 0
25 2 3 0
5 6 7
输出样例#1:
17

说明

对于10%的数据每个人都要求让全部n个人合影

对于30%的数据n<=15 m<=15

另有10%的数据答案为0

对于50%的数据n<=40 m<=40

另有10%的数据每个人只愿意拍一个人

对于100%的数据m,n<=100

思路:最大权闭合子图

m个请求与s连边,权值为方案价值;

n个员工与t连边,权值为员工花费;

方案与牵扯到的员工连边,权值为inf;

然后跑最大流,总流量就是最优方案的总花费;

然后,总收益=总价值-总花费。

代码实现:

 1 #include<cstdio>
 2 #include<cstring>
 3 #define inf 1000000
 4 #define maxn 300
 5 #define maxm 300000
 6 int n,m,s,t,tw,tot;
 7 int a,b,c;
 8 int d[maxn],q[maxn],head,tail;
 9 int h[maxn],hs=1;
10 struct edge{int s,n,w;}e[maxm];
11 inline int min(int x,int y){return x<y?x:y;}
12 void add(int q,int z,int w){
13     e[++hs]=(edge){z,h[q],w},h[q]=hs;
14     e[++hs]=(edge){q,h[z]},h[z]=hs;
15 }
16 void bfs(){
17     memset(d,0,sizeof(d));
18     head=tail=0;
19     q[head++]=s,d[s]=1;
20     while(head>tail){
21         a=q[tail++];
22         for(int i=h[a];i;i=e[i].n)
23         if(!d[e[i].s]&&e[i].w){
24             d[e[i].s]=d[a]+1;
25             if(e[i].s==t) return;
26             q[head++]=e[i].s;
27         }
28     }
29 }
30 int ap(int k,int w){
31     if(k==t) return w;
32     int uw=w;
33     for(int i=h[k];i&&uw;i=e[i].n)
34     if(e[i].w&&d[e[i].s]==d[k]+1){
35         int nw=ap(e[i].s,min(uw,e[i].w));
36         if(nw) e[i].w-=nw,e[i^1].w+=nw,uw-=nw;
37         else d[e[i].s]=0;
38     }
39     return w-uw;
40 }
41 void Dinic(){while(bfs(),d[t]) tw+=ap(s,inf);}
42 int main(){
43     scanf("%d%d",&m,&n);
44     s=0,t=n+m+1;
45     for(int i=1;i<=m;i++){
46         scanf("%d",&a);
47         tot+=a;
48         add(s,i,a);
49         while(scanf("%d",&a)&&a) add(i,a+m,inf);
50     }
51     for(int i=1;i<=n;i++) scanf("%d",&a),add(i+m,t,a);
52     Dinic();
53     printf("%d
",tot-tw);
54     return 0;
55 }

因为hs初值忘了赋一,挣扎了1h,我绝对要玩。

本来想水水网络流,增加一下信心,没想到第一个题就坑了,搞了1h,才用土法子拿了80分。

明明不是正解,还固执己见,看了人家的AC代码,还不想该。

最后又惊扰了师傅他老人家,才知道是最大权闭合子图的裸题。

 1 #include<cstdio>
 2 #include<cstring>
 3 #define inf 100000000
 4 #define maxn 300
 5 #define maxm 300000
 6 int n,m,s,t,tw;
 7 int a,b,c;
 8 int f[maxn],l[maxn],map[maxn][maxn];
 9 int d[maxn],q[maxn],head,tail;
10 int h[maxn],hs;
11 struct edge{int s,n,w;}e[maxm];
12 inline int min(int x,int y){return x<y?x:y;}
13 void bfs(){
14     memset(d,0,sizeof(d));
15     head=tail=0;
16     q[head++]=s,d[s]=1;
17     while(head>tail){
18         a=q[tail++];
19         for(int i=h[a];i;i=e[i].n)
20         if(!d[e[i].s]&&e[i].w){
21             d[e[i].s]=d[a]+1;
22             if(e[i].s==t) return;
23             q[head++]=e[i].s;
24         }
25     }
26 }
27 int ap(int k,int w){
28     if(k==t) return w;
29     int uw=w;
30     for(int i=h[k];i&&uw;i=e[i].n)
31     if(e[i].w&&d[e[i].s]==d[k]+1){
32         int nw=ap(e[i].s,min(uw,e[i].w));
33         if(nw) e[i].w-=nw,e[i^1].w+=nw,uw-=nw;
34         else d[e[i].s]=0;
35     }
36     return w-uw;
37 }
38 bool Dinic(){
39     bfs();
40     if(!d[t]) return false;
41     tw+=ap(s,inf);
42     return true;
43 }
44 int main(){
45     scanf("%d%d",&m,&n);
46     s=0,t=n+1;
47     for(int i=1;i<=m;i++){
48         while(scanf("%d",&map[i][l[i]])&&map[i][l[i]++]);
49         map[i][l[i]-1]=t;
50     }
51     for(int i=1;i<=n;i++) scanf("%d",&f[i]);
52     for(int i=1;i<=m;i++){
53         b=s;
54         for(int j=1;j<l[i]&&map[i][0];j++){
55             a=b,b=map[i][j],c=map[i][0];
56             e[++hs]=(edge){b,h[a],c},h[a]=hs;
57             e[++hs]=(edge){a,h[b]},h[b]=hs;
58             if(c>=f[b]) map[i][0]-=f[b],f[b]=0;
59             else f[b]-=c,map[i][0]=0;
60         }
61     }
62     while(Dinic());
63     printf("%d
",tw);
64     return 0;
65 }
问题代码(留作纪念)

完成了可以看一下原版

题目来源:洛谷

原文地址:https://www.cnblogs.com/J-william/p/6596496.html