题意
- 给我们 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 //宏定义免注释 freopen
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)
/*=========================ACMer===========================*/
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;
/* for_(i, 0, n) head[i] = 0; */
memset(head, 0, sizeof head);
}
int n, m, s, t;
int lv[mxn], cur[mxn]; //分层、当前弧优化
queue<int> q;
bool bfs()
{
/* for_(i, 0, t) lv[i] = -1, cur[i] = head[i]; */
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) //注意这里的返回的数据类型不能是 bool 哭。。。
{
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);
/*
2 6 7 9 18 19
1 3 4 5 6
12
*/
return 0;
}
版权声明:本文为qq_34261446原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。