AbOr's story --FOJ 1980

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;
}

 

posted @ 2010-10-11 16:09  勇泽  阅读(1146)  评论(2编辑  收藏  举报