说实话真是个神仙题,日常被网络流草
推两篇我个人觉得讲的不错的文章
详细版本
简明版本
然后当然我自己也要来一遍啦~
一眼看去,像极了最大权闭合子图
等等,但是药材是不收费的啊…
而且药品的收益有正有负…
限制条件是药品数等于药材数
先说连边方案
没有权值我们就人为加上一个权值
下面定义 p [ i ] p[i] p[i]为选择的收益(取反,因为负数的收益是正的)
s 向 药 品 i 连 流 量 p [ i ] + i n f 的 边 s向药品i连流量p[i]+inf的边 s向药品i连流量p[i]+inf的边
药 品 i 向 对 应 的 药 材 连 i n f 的 边 药品i向对应的药材连inf的边 药品i向对应的药材连inf的边
药 材 向 t 连 i n f 的 边 药材向t连inf的边 药材向t连inf的边
求最小割就可以解决问题(当然还不是答案)
Ⅰ . 首 先 , 最 小 割 不 会 割 药 品 和 药 材 之 间 的 边 \color{Red}Ⅰ.首先,最小割不会割药品和药材之间的边 Ⅰ.首先,最小割不会割药品和药材之间的边
很明显嘛,因为如果割了这种边,为什么不直接去割药材和 t t t之间的边呢?
割药材和 t t t的边等于是不选择这个药材,对最小割的贡献更大啊!!!
Ⅱ . 其 次 , 最 小 割 只 会 割 n 条 边 \color{Red}Ⅱ.其次,最小割只会割n条边 Ⅱ.其次,最小割只会割n条边
由于只会割 s s s到药品的边,和药材到 t t t的边
因为 s s s到药品连了 n n n条边,药材到 t t t连了 n n n条边
而且不会割 n + 1 n+1 n+1条边,因为权值都是 i n f inf inf级别的,不划算
Ⅲ . 这 个 最 小 割 有 什 么 性 质 呢 ? \color{Red}Ⅲ.这个最小割有什么性质呢? Ⅲ.这个最小割有什么性质呢?
割 掉 了 n 条 边 , 也 就 是 割 掉 了 x 个 药 品 ( 不 选 择 ) 和 n − x 个 药 材 ( 选 择 ) 割掉了n条边,也就是割掉了x个药品(不选择)和n-x个药材(选择) 割掉了n条边,也就是割掉了x个药品(不选择)和n−x个药材(选择)
也 就 是 n − x 个 药 品 选 择 了 , n − x 个 药 材 选 择 了 , 满 足 限 制 条 件 也就是n-x个药品选择了,n-x个药材选择了,满足限制条件 也就是n−x个药品选择了,n−x个药材选择了,满足限制条件
又 因 为 是 最 大 权 闭 合 子 图 的 求 法 , 所 以 选 择 某 个 药 品 , 相 关 的 药 材 都 会 被 选 择 又因为是最大权闭合子图的求法,所以选择某个药品,相关的药材都会被选择 又因为是最大权闭合子图的求法,所以选择某个药品,相关的药材都会被选择
非常惊人的想法呢
#include
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
const int inf=1e9;
int n,m,s,t;
int dis[maxn];
char a[109][109];
struct edge{
int to,nxt,flow;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int flow){
d[++cnt]=(edge){v,head[u],flow},head[u]=cnt;
d[++cnt]=(edge){u,head[v],0},head[v]=cnt;
}
bool bfs(int s,int t)
{
memset(dis,0,sizeof(dis));
dis[s]=1;
queueq; q.push( s );
while( !q.empty() )
{
int u=q.front(); q.pop();
for(int i=head[u];i;i=d[i].nxt )
{
int v=d[i].to;
if( d[i].flow&&dis[v]==0 )
{
dis[v]=dis[u]+1;
if( v==t ) return true;
q.push( v );
}
}
}
return false;
}
int dinic(int u,int t,int flow)
{
if( u==t ) return flow;
int res=flow;
for(int i=head[u];i&&res;i=d[i].nxt )
{
int v=d[i].to;
if( dis[v]==dis[u]+1&&d[i].flow)
{
int temp=dinic(v,t,min(res,d[i].flow) );
if( temp==0 ) dis[v]=0;
res-=temp;
d[i].flow-=temp;
d[i^1].flow+=temp;
}
}
return flow-res;
}
int main()
{
cin >> n;
s=0,t=n+n+1;
for(int i=1;i<=n;i++)
{
int x; cin >> x;
while( x-- )
{
int num; cin >> num;
add(i,num+n,inf);
}
}
ll sumn=(ll)inf*n,ans=0;
for(int i=1;i<=n;i++)
{
int f; cin >> f;
f=-f;//边权取反,变成正边权
sumn+=f;
add(s,i,f+inf);//源点连向药品
add(i+n,t,inf);//药材连向汇点
}
while( bfs(s,t) ) ans+=dinic(s,t,inf);
cout << -(sumn-ans);
}