题意
给我们 m 个实验,n 个实验仪器,每个实验需要 n 个仪器中的一部分(看做图中的边),每个仪器如果被使用要先被购买,而被购买的时候需要花上一定的费用,而我们做某个实验会得到特定的收益。 现在让我们合理的选择一些实验,是使净收益最大,输出做大的值和选择了那些实验, 以及实验的用的仪器。
思路
首先介绍什么是:
“最大权闭合子图”,简单说就是我们在一个图有向图中选择一个点,而这些点指向的下一个点也在我们所选择的集合中。 对应到我们这题里就是我们我们要选择做某个实验就要,就要选(买)这个实验所要求的仪器。 怎么求?结论:最大闭合权 = 与 S 超级源点相连的边权和 - 这个图中的最小割。
证明传送🚪 其他补充
怎么建图,求最小割
虚拟出一个超级源点 S,令 S 与每个实验之间连接一条容量为 这个实验的的收益 的一条边。 虚拟出一个超级汇点 T,令每个实验仪器看做一个点,与 T 连一条 容量为仪器费用的边。 如果实现分别需要与的仪器连接一条容量为 INF 的边。 最后跑 dinic 最后输出要求的输出选则的实验有哪些?这个怎么求,这个只需要判读 当前这个实验 x 是否没被割掉,即还能被访问到 lv [x] != -1, 就行了
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <map>
#include <bitset>
#include <vector>
using namespace std;
void fre ( ) { system ( "clear" ) , freopen ( "A.txt" , "r" , stdin ) ; freopen ( "Ans.txt" , "w" , stdout ) ; }
void Fre ( ) { system ( "clear" ) , freopen ( "A.txt" , "r" , stdin ) ; }
void Run ( int x = 0 ) {
#ifdef ACM
if ( ! x) fre ( ) ; else Fre ( ) ;
#endif
}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<ll, ll>
#define m_p make_pair
#define for_(i, s, e) for(ll i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(ll i = (ll)(e); i >= (ll)(s); i --)
#define memset(a, b, c) memset(a, (int)b, c);
#define size() size() * 1LL
#define sc scanf
#define pr printf
#define sd(a) sc("%lld", &a)
#define ss(a) sc("%s", a)
#define __ pr( "------------------------\n" );
#define ___ pr("\n------------------------\n");
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
const int mxn = 2e4 ;
const int mxm = 1e5 ;
struct Edge
{
int v, w, next;
} edge[ mxm] ;
int head[ mxn] , tot;
void Add ( int u, int v, int w)
{
edge[ ++ tot] = ( Edge) { v, w, head[ u] } ; head[ u] = tot;
edge[ ++ tot] = ( Edge) { u, 0 , head[ v] } ; head[ v] = tot;
}
void init ( int n)
{
tot = 1 ;
memset ( head, 0 , sizeof head) ;
}
int n, m, s, t;
int lv[ mxn] , cur[ mxn] ;
queue< int > q;
bool bfs ( )
{
memset ( lv, - 1 , sizeof lv) ;
memcpy ( cur, head, sizeof head) ;
lv[ s] = 0 ;
q. push ( s) ;
while ( q. size ( ) )
{
int u = q. front ( ) ; q. pop ( ) ;
for ( int i = head[ u] ; i; i = edge[ i] . next)
{
int v = edge[ i] . v, w = edge[ i] . w;
if ( w > 0 && lv[ v] == - 1 )
{
lv[ v] = lv[ u] + 1 ;
q. push ( v) ;
}
}
}
return lv[ t] != - 1 ;
}
int dfs ( int u = s, int flow = inf)
{
if ( u == t) return flow;
int rmn = flow;
for ( int i = cur[ u] ; i && rmn; i = edge[ i] . next)
{
cur[ u] = i;
int v = edge[ i] . v, w = edge[ i] . w;
if ( w > 0 && lv[ v] == lv[ u] + 1 )
{
int c = dfs ( v, min ( w, rmn) ) ;
rmn - = c;
edge[ i] . w - = c;
edge[ i ^ 1 ] . w + = c;
}
}
if ( flow - rmn == 0 ) lv[ u] = - inf;
return flow - rmn;
}
ll dinic ( )
{
ll ans = 0 , c;
while ( bfs ( ) )
{
while ( ( c = dfs ( ) ) > 0 ) ans + = c;
}
return ans;
}
int main ( )
{
Run ( ) ;
sc ( "%d %d" , & m, & n) ;
s = 0 , t = t + 100 ;
init ( t) ;
ll sum = 0 ;
int x;
for_ ( i, 1 , m)
{
sc ( "%d" , & x) ;
sum + = x;
Add ( s, i, x) ;
while ( getchar ( ) == ' ' )
{
sc ( "%d" , & x) ;
Add ( i, x + m, inf) ;
}
}
for_ ( i, m + 1 , m + n)
{
sc ( "%d" , & x) ;
Add ( i, t, x) ;
}
ll ans = sum - dinic ( ) ;
map< int , int > mp, np;
for ( int i = head[ s] ; i; i = edge[ i] . next)
{
int v = edge[ i] . v, w = edge[ i] . w;
if ( lv[ v] != - 1 && lv[ v] != - inf)
{
mp[ v] = 1 ;
for ( int j = head[ v] ; j; j = edge[ j] . next)
{
if ( edge[ j] . v != s) np[ edge[ j] . v - m] = 1 ;
}
}
}
int flag = 1 ;
for ( auto x : mp)
{
if ( flag)
pr ( "%d" , x. fi) , flag = 0 ;
else
pr ( " %d" , x. fi) ;
}
pr ( "\n" ) ;
flag = 1 ;
for ( auto x : np)
{
if ( flag)
pr ( "%d" , x. fi) , flag = 0 ;
else
pr ( " %d" , x. fi) ;
}
pr ( "\n" ) ;
pr ( "%lld" , ans) ;
return 0 ;
}