描述
周末,小Hi和小Ho所在的班级决定举行一些班级建设活动。
根据周内的调查结果,小Hi和小Ho一共列出了N项不同的活动(编号1..N),第i项活动能够产生a[i]的活跃值。
班级一共有M名学生(编号1..M),邀请编号为i的同学来参加班级建设活动需要消耗b[i]的活跃值。
每项活动都需要某些学生在场才能够进行,若其中有任意一个学生没有被邀请,这项活动就没有办法进行。
班级建设的活跃值是活动产生的总活跃值减去邀请学生所花费的活跃值。
小Hi和小Ho需要选择进行哪些活动,来保证班级建设的活跃值尽可能大。
比如有3项活动,4名学生:
第1项活动产生5的活跃值,需要编号为1、2的学生才能进行;
第2项活动产生10的活跃值,需要编号为3、4的学生才能进行;
第3项活动产生8的活跃值,需要编号为2、3、4的学生才能进行。
编号为1到4的学生需要消耗的活跃值分别为6、3、5、4。
假设举办活动集合为{1},需要邀请的学生集合为{1,2},则得到的班级活跃值为5-9 = -4。
假设举办活动集合为{2},需要邀请的学生集合为{3,4},则得到的班级活跃值为10-9 = 1。
假设举办活动集合为{2,3},需要邀请的学生集合为{2,3,4},则得到的班级活跃值为18-12 = 6。
假设举办活动集合为{1,2,3},需要邀请的学生集合为{1,2,3,4},则得到的班级活跃值为23-18 = 5。
小Hi和小Ho总是希望班级活跃值越大越好,因此在这个例子中,他们会选择举行活动2和活动3。
输入
第1行:两个正整数N,M。1≤N≤200,1≤M≤200
第2行:M个正整数,第i个数表示邀请编号为i的学生需要花费的活跃值b[i],1≤b[i]≤1,000
第3..N+2行:第i行表示编号为i的活动情况。首先是2个整数a,k,a表示该活动产生的活跃值,k表示该活动需要的学生人数。接下来k个整数列举该活动需要的学生编号。1≤a≤1,000,1≤k≤M
输出
第1行:1个整数,最大可以产生的班级活跃值
3 4 6 3 5 4 5 2 1 2 10 2 3 4 8 3 2 3 4样例输出
6
网络流的模板题,网络流解决 最大权闭合子图的问题。
这个问题一般是要分好网络的流量,最后答案为与s相连的所有价值之和sum-最大流。因为这个题是最小割的问题,求出来的最大流=最小割,总价值-花费的价值就是答案,除了与s和t点连的边之外,其他边都是价值无穷,这样最小割就不会切错了。
建模,s与活动相连 ,活动与学生连 w为无穷,学生与t相连。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define REP(I, X) for(int I = 0; I < X; ++I)
#define FF(I, A, B) for(int I = A; I <= B; ++I)
#define clear(A, B) memset(A, B, sizeof A)
#define copy(A, B) memcpy(A, B, sizeof A)
#define min(A, B) ((A) < (B) ? (A) : (B))
#define max(A, B) ((A) > (B) ? (A) : (B))
using namespace std;
typedef long long ll;
typedef long long LL;
const int oo = 0x3f3f3f3f;
const int maxE = 1100050;
const int maxN = 10005;
const int maxQ = 1100050;
struct Edge{
int v, n;
long long c;
};
Edge edge[maxE];
int adj[maxN], cntE;
int Q[maxQ], head, tail, inq[maxN];
int d[maxN], num[maxN], cur[maxN], pre[maxN];
int s, t, nv;
int n, m, nm;
int path[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
void addedge(int u, int v, long long c){
edge[cntE].v = v;edge[cntE].c = c; edge[cntE].n = adj[u]; adj[u] = cntE++;
edge[cntE].v = u;edge[cntE].c = 0; edge[cntE].n = adj[v]; adj[v] = cntE++;
}
void REV_BFS(){
clear(d, -1);
clear(num, 0);
head = tail = 0;
d[t] = 0;
num[0] = 1;
Q[tail++] = t;
while(head != tail){
int u = Q[head++];
for(int i = adj[u]; ~i; i = edge[i].n){
int v = edge[i].v;
if(~d[v]) continue;
d[v] = d[u] + 1;
num[d[v]]++;
Q[tail++] = v;
}
}
}
long long ISAP(){
copy(cur, adj);
REV_BFS();
int u = pre[s] = s, i;
long long flow=0;
while(d[s] < nv){
if(u == t){
long long f = oo, neck;
for(i = s; i != t; i = edge[cur[i]].v){
if(f > edge[cur[i]].c){
f = edge[cur[i]].c;
neck = i;
}
}
for(i = s; i != t; i = edge[cur[i]].v){
edge[cur[i]].c -= f;
edge[cur[i] ^ 1].c += f;
}
flow += f;
u = neck;
}
for(i = cur[u]; ~i; i = edge[i].n) if(edge[i].c && d[u] == d[edge[i].v] + 1) break;
if(~i){
cur[u] = i;
pre[edge[i].v] = u;
u = edge[i].v;
}
else{
if(0 == (--num[d[u]])) break;
int mind = nv;
for(i = adj[u]; ~i; i = edge[i].n){
if(edge[i].c && mind > d[edge[i].v]){
mind = d[edge[i].v];
cur[u] = i;
}
}
d[u] = mind + 1;
num[d[u]]++;
u = pre[u];
}
}
return flow;
}
int read () {
char c = ' ' ;
int x = 0 ;
while ( c < '0' || c > '9' )
c = getchar () ;
while ( c >= '0' && c <= '9' ) {
x = x * 10 + c - '0' ;
c = getchar () ;
}
return x ;
}
int w[maxN];
void work()
{
cntE=0;
clear(adj, -1);
int u,v;
int sum=0;
for(int i=1;i<=m;i++)
{
w[i]=read () ;
}
s = 0 , t = n+m+1, nv = t + 1 ;
int qq;
for(int i=1;i<=n;i++){
u = read () ;v = read () ;
addedge ( s , i , u ) ;
sum+=u;
for(int j=1;j<=v;j++)
{
qq=read ();
addedge ( i , n+qq , oo ) ;
}
}
for(int i=1;i<=m;i++)
{
addedge ( n+i , t , w[i] ) ;
}
LL flow =ISAP();
printf ( "%lld\n",sum-flow) ;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
work();
}
return 0;
}