我发现很多人关于输出方案那个地方解释都是错的,这不是误导别人吗!!!
能ac又怎样啊…
还有人说什么因为左边没满流,所以右边满流…
第一问,求最小代价
由 于 最 后 要 把 每 条 边 都 去 掉 由于最后要把每条边都去掉 由于最后要把每条边都去掉
去 掉 一 条 a − b 的 边 , 要 么 把 a 的 出 边 去 掉 , 要 么 把 b 的 入 边 去 掉 去掉一条a-b的边,要么把a的出边去掉,要么把b的入边去掉 去掉一条a−b的边,要么把a的出边去掉,要么把b的入边去掉
可 以 发 现 这 不 就 是 二 分 图 吗 ? ? ! ! 可以发现这不就是二分图吗??!! 可以发现这不就是二分图吗??!!
我 们 把 每 个 点 拆 分 为 出 点 ( 边 从 这 个 点 出 去 ) 和 入 点 ( 边 从 这 个 点 进 来 ) 我们把每个点拆分为出点(边从这个点出去)和入点(边从这个点进来) 我们把每个点拆分为出点(边从这个点出去)和入点(边从这个点进来)
出 点 放 在 二 分 图 左 边 , 和 源 点 连 一 条 代 价 为 b i 的 边 出点放在二分图左边,和源点连一条代价为b_i的边 出点放在二分图左边,和源点连一条代价为bi的边
入 点 放 在 二 分 图 右 边 , 和 汇 点 连 一 条 代 价 为 a i 的 边 入点放在二分图右边,和汇点连一条代价为a_i的边 入点放在二分图右边,和汇点连一条代价为ai的边
对 于 a − b 边 , a 的 出 点 和 b 的 入 点 连 一 条 代 价 无 穷 的 边 ( 保 证 最 小 割 不 会 割 这 条 边 ) 对于a-b边,a的出点和b的入点连一条代价无穷的边(保证最小割不会割这条边) 对于a−b边,a的出点和b的入点连一条代价无穷的边(保证最小割不会割这条边)
跑 最 大 流 ( 最 小 割 ) 就 是 答 案 跑最大流(最小割)就是答案 跑最大流(最小割)就是答案
为 什 么 ? \color{Red}为什么? 为什么?
最 小 割 保 证 s 不 能 到 t 最小割保证s不能到t 最小割保证s不能到t
所 以 每 条 边 a − b 要 么 是 源 点 和 a 的 出 点 的 边 被 割 掉 所以每条边a-b要么是源点和a的出点的边被割掉 所以每条边a−b要么是源点和a的出点的边被割掉
要 么 是 b 的 入 点 和 汇 点 的 边 被 割 掉 ( 如 果 都 不 被 割 , 那 么 源 点 能 到 汇 点 , 不 是 最 小 割 ) 要么是b的入点和汇点的边被割掉(如果都不被割,那么源点能到汇点,不是最小割) 要么是b的入点和汇点的边被割掉(如果都不被割,那么源点能到汇点,不是最小割)
这 就 符 合 所 有 边 都 会 被 割 掉 , 因 为 是 最 小 割 , 所 以 代 价 最 小 这就符合所有边都会被割掉,因为是最小割,所以代价最小 这就符合所有边都会被割掉,因为是最小割,所以代价最小
第二问
你 可 能 会 想 到 满 流 的 边 一 定 是 割 你可能会想到满流的边一定是割 你可能会想到满流的边一定是割
但 满 流 的 边 只 是 割 边 的 必 要 条 件 但满流的边只是割边的必要条件 但满流的边只是割边的必要条件
一 条 边 是 割 边 , 满 足 满 流 且 左 右 端 点 互 相 不 可 达 到 一条边是割边,满足满流且左右端点互相不可达到 一条边是割边,满足满流且左右端点互相不可达到
所 以 从 源 点 d f s , 只 走 没 有 满 流 的 边 ( 标 记 能 到 的 点 ) 所以从源点dfs,只走没有满流的边(标记能到的点) 所以从源点dfs,只走没有满流的边(标记能到的点)
由 于 s 能 到 的 点 都 在 s 集 合 中 , 到 不 了 的 点 都 在 t 集 合 中 由于s能到的点都在s集合中,到不了的点都在t集合中 由于s能到的点都在s集合中,到不了的点都在t集合中
所 以 枚 举 每 条 边 判 断 即 可 所以枚举每条边判断即可 所以枚举每条边判断即可
#include <iostream>
#include <queue>
using namespace std;
const int maxn=2e5+10;
const int inf=1e9;
int n,m,s,t,sumn,dis[maxn],a[maxn],b[maxn],id[maxn];
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()
{
for(int i=0;i<=t;i++) dis[i]=0;
dis[s]=1;
queue<int>q; 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 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,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;
}
vector<int>vec;
int ok[maxn];
void dfs(int u)
{
for(int i=head[u];i;i=d[i].nxt )
{
int v=d[i].to;
if( ok[v]||d[i].flow==0 ) continue;
ok[v]=1; dfs(v);
}
}
int main()
{
cin >> n >> m;
s=0,t=n+n+1;
for(int i=1;i<=n;i++) cin >> a[i];
for(int i=1;i<=n;i++) cin >> b[i];
for(int i=1;i<=m;i++)
{
int l,r; cin >> l >> r;
add(l+n,r,inf);//l+n是出边,r是入边
}
for(int i=1;i<=n;i++)
{
add(s,i+n,b[i]);//出边被割掉,需要获得b[i]元
add(i,t,a[i] );//入边被割掉
}
int ans=0,shu=0;
while( bfs() ) ans+=dinic(s,inf);
cout << ans << '\n';
dfs(s); ok[s]=1;
for(int i=1;i<=n;i++)
{
if( ok[s]!=ok[i+n] ) shu++;
if( ok[t]!=ok[i] ) shu++;
}
cout << shu << '\n';
for(int i=1;i<=n;i++)
{
if( ok[s]!=ok[i+n] ) cout << i << " -\n";
if( ok[t]!=ok[i] ) cout << i << " +\n";
}
}