题目描述
此时的托米老师已经出任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 ;
}

本文介绍了一个基于最大权闭合子图模型的算法,用于解决如何利用优惠券等支付手段达到购物支付最小化现金的目标。通过建立合适的网络流模型并运行最大流算法,找到最优支付方案。
616

被折叠的 条评论
为什么被折叠?



