题目描述
此时的托米老师已经出任CEO,迎娶白富美,走向了人生巅峰!于是这个暑假,托米老师打算在北京一个偏僻的小农村里度过他的假期。
由于这里什么都没有,于是他去超市选了很多生活用品,更多的是吃的,然后推着堆满零食的购物车到柜台等待结账。
当然,我们都知道他的钱包里有很多钱。但是,作为一名为生活精打细算的男孩子,他更愿意使用其他支付方式如:饭券,礼券,不同类型的优惠券等。但是饭券只能用于购买食物,而礼券通常只限于某种类型的礼物。
现在给你托米购物车中物品的数量N和每件物品的价格。也会给出他钱包中的代金券数量M以及允许使用的信息 。
在为他的购物付款时,托米可能使用代金券的金额超过他所购物品的成本。也可以在多张代金券之间拆分商品的成本,并使用代金券支付多件商品。
请你计算托米需要为购物支付的额外现金的最小金额。
输入描述:
输入的第一行包含一个整数T,用于指定测试用例的数量。
每个测试用例前面都有一个空白行。
每个测试用例从包含两个正整数N(物品数量)和M(券数量)的行开始。
接下来一行包含N个数字,第i个数字表示托米购物车里第i件物品的价格。
接下来一行包含M个数字,第i个数字表示第i张券的金额。
接下来有M行,当中的第 i 行描述第 i 张卷可以买哪些商品。每行的第一个数字是 K,代表第 i 张卷可以为 K 件商品付款,接下来还有 K 个数,是这 K 件商品的编号
输出描述:
对于每个测试用例输出数字,表示托米需要支付多少现金。
示例1
输入
1
3 2
15 20 10
20 30
3 1 2 3
1 3
输出
15
备注:
T≤40 N≤200 M≤1200
满足M>200的数据保证只有一组
单个物品金额和单张优惠券金额≤10000
题目大概:
中文题目好理解。
思路:
这是最小割模型中的最大权闭合子图。直接按照模型的建边方法,连边。跑一边最大流就可以了。
复习一遍最大权闭合子图了。
代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std ; #define RPEF( i , a , b ) for ( int i = a ; i <= b ; ++ i ) #define REP( i , n ) for ( int i = 0 ; i < n ; ++ i ) #define copy( a , x ) memcpy ( a , x , sizeof a ) typedef long long LL ; const int MAXN = 1500 ; const int MAXE = 1000000 ; const int MAXQ = 1000000 ; const LL INF = 1e15 ; struct Edge { int v , n ; LL c ; Edge ( int var = 0 , LL cap = 0 , int next = 0 ) : v ( var ) , c ( cap ) , n ( next ) {} } ; struct netWork { Edge edge[MAXE] ; int adj[MAXN] , cntE ; int cur[MAXN] , d[MAXN] , num[MAXN] , pre[MAXN] ; bool vis[MAXN] ; int Q[MAXQ] , head , tail ; int s , t , nv ; LL flow ; void init () { cntE = 0 ; memset(adj,-1,sizeof(adj)); } void addedge ( int u , int v , LL c , LL rc = 0 ) { edge[cntE] = Edge ( v , c , adj[u] ) ; adj[u] = cntE ++ ; edge[cntE] = Edge ( u , rc , adj[v] ) ; adj[v] = cntE ++ ; } void rev_Bfs () { memset(vis,0,sizeof(vis)); memset(num,0,sizeof(num)); d[t] = 0 ; vis[t] = 1 ; head = tail = 0 ; Q[tail ++] = t ; num[0] = 1 ; while ( head != tail ) { int u = Q[head ++] ; for ( int i = adj[u] ; ~i ; i = edge[i].n ) { int v = edge[i].v ; if ( vis[v] ) continue ; vis[v] = 1 ; d[v] = d[u] + 1 ; ++ num[d[v]] ; Q[tail ++] = v ; } } } LL ISAP () { copy ( cur , adj ) ; rev_Bfs () ; flow = 0 ; int i , u = pre[s] = s ; while ( d[s] < nv ) { if ( u == t ) { LL f = INF ; int pos ; for ( i = s ; i != t ; i = edge[cur[i]].v ) if ( f > edge[cur[i]].c ) f = edge[cur[i]].c , pos = i ; for ( i = s ; i != t ; i = edge[cur[i]].v ) edge[cur[i]].c -= f , edge[cur[i] ^ 1].c += f ; u = pos ; flow += f ; } 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 mmin = nv ; for ( i = adj[u] ; ~i ; i = edge[i].n ) if ( edge[i].c && mmin > d[edge[i].v] ) cur[u] = i , mmin = d[edge[i].v] ; d[u] = mmin + 1 ; ++ num[d[u]] ; u = pre[u] ; } } return flow ; } } ; netWork net ; int n,m; int a[300]; int b[1300]; long long sum=0; vector<int>G[1300]; void work () { int d , u , v , c ; net.init () ; net.s = 0 , net.t = n+m+1, net.nv = net.t + 1 ; for(int i=1;i<=n;i++) { net.addedge(net.s,i,a[i]); } for(int i=1;i<=m;i++) { net.addedge(n+i,net.t,b[i]); } for(int i=1;i<=m;i++) { for(int j=0;j<G[i].size();j++) { int v=G[i][j]; net.addedge(v,n+i,INF); } } LL flow = net.ISAP () ; printf ( "%lld\n" , sum-flow) ; } int main () { int T ; scanf ( "%d" , &T ) ; while ( T -- ) { sum=0; memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++)G[i].clear(); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); sum+=a[i]; } for(int i=1;i<=m;i++) { scanf("%d",&b[i]); } for(int i=1;i<=m;i++) { int q; scanf("%d",&q); for(int j=1;j<=q;j++) { int u; scanf("%d",&u); G[i].push_back(u); } } work () ; } return 0 ; }