1、题目类型:图论、最大流、最小切割最大流定理、Dinic算法。
2、解题思路:《算法艺术与信息学竞赛》中只是换了一种说明的原题(P317)。(1)构建二分图G,它的X结点为所有的宝石,每个宝石 i 用 Xi 来表示,连接一条容量为 Ii 的弧S-Yi;它的Y结点为所有的女孩,每个女孩 i 用 Yi 来表示,连接一条容量为Ei的弧Yi-T。(2)最小切割最大流定理(证明),步骤1:最小切割不可能包含无限容量的边。这是显然的,因此如果在T集中的任何一个女孩 Ej 需要宝石 Ij ,那么 Ij 一定也在T集中,它保证了切割一定对应一个可行方案。步骤2:对于任何一个宝石 i ,如果宝石在T集合中,说明该宝石应当购买,这时容量 Ii 被计算在了切割中;对于任何一个女孩 j ,如果女孩在集合T中,说明女孩被选择,这时容量 Ej 并没有计算在切割中,这样,该方案所对应的切割数值等于 sum{Ii}+sum{Ej};(3)若所有女孩的总收益E,最大收益=E-(sum{Ii}+sum{Ej})。
3、注意事项:注意最大流的求解,此题只有Dinic算法符合题意,Ford-Fulkerson算法、SAP算法TLE。
4、实现方法:
#include < iostream >
#define MAXN 20010
#define MAXM 200000
#define INIFINITY 10000000000
using namespace std;
typedef __int64 FLOW;
struct Edge { // v:adjacent to, r: remains, c: capacity
int v;
FLOW r, c;
Edge * next; // adjacent list
Edge * Reverse(); // get the pointer of reverse edge
};
struct Vertex {
Edge * first; // first adjacent edge of the vertex
};
Edge edges[MAXM]; // edges's list
Vertex g[MAXN]; // vertexes's list
int n, m, s, t; // vertex's number, edges's number, source, terminal
int dest; // the vertex ending the dfs process
int d[MAXN]; // the level of the vertex(dinic); the distance to t (SAP)
// get the reference of the reverse edge of it
Edge inline * Edge::Reverse()
{
return edges + (( this - edges) ^ 1 );
}
void AddEdge( int u, int v, FLOW c)
{ // add sigle edge to network
edges[m].v = v;
edges[m].r = c; // notation: use .r or use .c
edges[m].next = g[u].first;
g[u].first = edges + m;
m ++ ;
}
void Insert( int u, int v, FLOW c1, FLOW c2)
{ // insert a two-way edge
AddEdge(u, v, c1);
AddEdge(v, u, c2);
}
// bfs to construct the level graph
bool BFS_Dinic( int s, int t, int n)
{
int Q[MAXN]; // queue to construct level graph
int front, rear, u;
for (u = 0 ; u < n; u ++ ) d[u] = n;
front = rear = 0 ;
Q[rear ++ ] = t;
d[t] = n - 1 ;
while (front < rear)
{
u = Q[front ++ ];
for (Edge * p = g[u].first; p; p = p -> next)
{
if (p -> Reverse() -> r > 0 && d[p -> v] == n)
{
d[p -> v] = d[u] - 1 ;
Q[rear ++ ] = p -> v;
if (p -> v == s) return true ;
}
} // for*/
} // while
return false ;
}
// sub function of Dinic_DFS
FLOW DFS_Dinic(Edge * e, FLOW flow)
{
FLOW f = 0 ;
if (e -> r < flow) flow = e -> r;
if (e -> v == dest)
f = flow;
else
{
for (Edge * p = g[e -> v].first; p && (f < flow); p = p -> next)
{ // f < flow is important
if (p -> r > 0 && d[p -> v] > d[e -> v])
{ // -> d[p->v] == d[e->v] + 1
f += DFS_Dinic(p, flow - f);
}
}
}
e -> r -= f;
e -> Reverse() -> r += f;
return f;
}
// main function of dinic algorithm
FLOW Dinic( int s, int t, int n)
{
FLOW maxflow = 0 ;
Edge * first = edges + m + 1 ;
first -> v = s;
dest = t;
while ( true ) {
first -> r = INIFINITY;
if ( ! BFS_Dinic(s, t, n)) break ; // t not in residual network
maxflow += DFS_Dinic(first, INIFINITY);
}
return maxflow;
}
int wm,bs;
FLOW sum;
void Init()
{
int i,j,b,c,cnt;
scanf( " %d%d " , & wm, & bs);
s = 0 ;m = 0 ;sum = 0 ;
t = wm + bs + 1 ;n = t + 1 ;
for (i = 0 ;i < n;i ++ )
g[i].first = NULL;
for (i = 1 ;i <= wm;i ++ )
{
scanf( " %d " , & cnt);
for (j = 0 ;j < cnt;j ++ )
{
scanf( " %d " , & b);
Insert(b,i + bs,INIFINITY, 0 );
}
scanf( " %d " , & c);
sum += c;
Insert(i + bs,t,c, 0 );
}
for (j = 1 ;j <= bs;j ++ )
{
scanf( " %d " , & c);
Insert(s,j,c, 0 );
}
}
int main()
{
int T, ca = 1 ;
scanf( " %d " , & T);
while (T -- )
{
Init();
sum -= Dinic(s,t,n);
printf( " Case %d: %I64d\n " ,ca ++ ,sum);
}
return 0 ;
}