有一定难度的一道题目,首先反驳一下网上有人讲的使用刘汝佳写的最小费用最大流的代码会超时的观点,我自己的写的并没有超时,而且用的还是cin和cout。虽然一开始也以为是效率问题,后来调试代码才发现,这个题目的oj判断很奇怪,如果出现了小数点后面的部分不符合标准答案不会报答案错误,而是直接报出超时。好的,啰嗦了那么多现在转入正题。首先是按照紫书的思路,讲题目所讲的问题抽象成为最大费用循环流的问题,然后将各个边的权值取反,那么就变成了最小费用流问题,因为负数的最小值对应的最大值最大,这样就很靠近我们所要使用的算法。但是在将所有的边的权值取反之后又会出现这样一个问题:还是有部分的边的权值为负,那么紫书上提了一种转化方法,大家可以直接参考这里就不详细叙述,其实这种办法的中心思想在于首先我们将所有的权值为负数的边都选上了,所以在代码中我们首先要计算所有的权值为负数的边之和(这个和也是负数),然后按照最小费用最大流的算法,将哪些不能够组成圈的负值边对应的值都找出来,然后用之前求出的和将这些不合要求的负值都剔除掉,那么反应到代码中就是 -(sum+cost),其他的具体实现见如下代码,一共两种方式,第一种是利用刘汝佳的方法,另一种是不适用vector,用的都是基本的数组,具体实现如下(如果有问题,欢迎指正):
#include<iostream>
#include<vector>
#include<string>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<sstream>
#include<cstdio>
#include<deque>
#include<functional>
using namespace std;
class Point{
public:
double x, y;
};
class Edge{
public:
int from, to, cap, flow;
double cost;
Edge(int fr, int t, int c, int fl, double co) :from(fr), to(t), cap(c), flow(fl), cost(co){}
};
int area[110][110];
const int Inf = 0x3f3f3f3f;
const double eps = 1e-6;
class Solve{
public:
int n;
double X, Y;
double sum;
vector<Edge> edge;
vector<Point> point;
vector<int> G[110];
void addEdge(int from, int to, int cap, double cost){
edge.push_back(Edge(from, to, cap, 0, cost));
edge.push_back(Edge(to, from, 0, 0, -cost));
int m = edge.size();
G[from].push_back(m - 2);
G[to].push_back(m - 1);
}
double dist(int i, int j){
double length = sqrt((point[i].x - point[j].x)*(point[i].x - point[j].x) +
(point[i].y - point[j].y)*(point[i].y - point[j].y));
return length;
}
void Init(){
point.clear();
edge.clear();
for (int i = 0; i < 110; i++) G[i].clear();
cin >> X >> Y;
sum = 0;
memset(area, 0, sizeof(area));
for (int i = 0; i < n; i++){
Point temp;
cin >> temp.x >> temp.y;
int t;
while (cin >> t){
if (t == 0) break;
t--;//转化为以0开始
area[i][t] = 1;
}
point.push_back(temp);
}
int in[110], out[110];
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
if (area[i][j]){
double co = -(dist(i, j)*X - Y);
if (co > 0){
addEdge(i, j, 1, co);
}
else{
addEdge(j, i, 1, -co);
out[j]++;
in[i]++;
sum += co;
}
}
}
}
for (int i = 0; i < n; i++){
if (in[i] > out[i]){//入度大于出度
addEdge(i, n + 1, in[i] - out[i], 0);
}
if (out[i]>in[i]){//出度大于入度
addEdge(n, i, out[i] - in[i], 0);
}
}
}
bool BellmanFloyd(int start, int end, double& flow, double& cost){
int inq[110];
double price[110];
int Flow[110];
int parent[110];
Flow[start] = Inf;
for (int i = 0; i < 110; i++) price[i] = 1000000;
price[start] = 0;
memset(inq, 0, sizeof(inq));
inq[start] = 1;
queue<int> q;
q.push(start);
while (!q.empty()){
int id = q.front();
q.pop();
inq[id] = 0;
for (int i = 0; i < G[id].size(); i++){
int ide = G[id][i];
int to = edge[ide].to;
if (edge[ide].cap > edge[ide].flow&&price[to] > price[id] + edge[ide].cost){
Flow[to] = min(Flow[id], edge[ide].cap - edge[ide].flow);
price[to] = price[id] + edge[ide].cost;
parent[to] = ide;
if (!inq[to]){
q.push(to);
inq[to] = 1;
}
}
}
}
if (price[end] == 1000000) return false;
flow += Flow[end];
cost += Flow[end] * price[end];
for (int i = end; i != start; i = edge[parent[i]].from){
edge[parent[i]].flow += Flow[end];
edge[parent[i] ^ 1].flow -= Flow[end];
}
return true;
}
double MaxFlow(int start, int end){
double flow = 0, cost = 0;
while (BellmanFloyd(start, end, flow, cost));
return cost;
}
double Deal(){
Init();
double res = MaxFlow(n, n + 1);
return -(sum + res) + eps;
}
};
int main(){
Solve a;
int Case = 0;
while (cin >> a.n){
Case++;
if (a.n == 0) break;
double res = a.Deal();
cout << "Case " << Case << ": " << setprecision(2) << fixed << res << endl;
}
return 0;
}
#include<iostream>
#include<vector>
#include<string>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<sstream>
#include<cstdio>
#include<deque>
#include<functional>
using namespace std;
class Point{
public:
double x, y;
};
class Edge{
public:
int from, to, cap, flow,next;
double cost;
Edge(int fr=0, int t=0, int c=0, int fl=0, double co=0) :from(fr), to(t), cap(c), flow(fl), cost(co){}
};
int area[110][110];
const int Inf = 0x3f3f3f3f;
const double eps = 1e-6;
Edge edge[2000000];
int q[2000000];
class Solve{
public:
int n;
double X, Y;
double sum;
Point point[110];
int G[110], amount_e,amount_p;//amount_e代表的是边的个数
//amount_p 代表的是点的个数
void addEdge(int from,int to,int cap,double cost){
edge[amount_e++]=Edge(from,to,cap,0,cost);
edge[amount_e++]=Edge(to, from, 0, 0, -cost);
edge[amount_e - 2].next = G[from];
G[from] = amount_e - 2;
edge[amount_e - 1].next = G[to];
G[to] = amount_e - 1;
}
double dist(int i,int j){
double length = sqrt((point[i].x - point[j].x)*(point[i].x - point[j].x) +
(point[i].y - point[j].y)*(point[i].y - point[j].y));
return length;
}
void Init(){
amount_e = 0;
amount_p = 0;
memset(G, -1, sizeof(G));
scanf("%lf %lf",&X,&Y);
sum = 0;
memset(area,0,sizeof(area));
for (int i = 0; i < n; i++){
Point temp;
temp.x = 0, temp.y = 0;
scanf("%lf %lf",&temp.x,&temp.y);
int t;
while (scanf("%d",&t)){
if (t == 0) break;
t--;//转化为以0开始
area[i][t] = 1;
}
point[amount_p++]=temp;
}
int in[110], out[110];
memset(in,0,sizeof(in));
memset(out, 0, sizeof(out));
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
if (area[i][j]){
double co = -(dist(i, j)*X - Y);
if (co > 0){
addEdge(i, j, 1, co);
}
else{
addEdge(j, i, 1, -co);
out[j]++;
in[i]++;
sum += co;
}
}
}
}
for (int i = 0; i < n; i++){
if (in[i] > out[i]){//入度大于出度
addEdge(i, n + 1, in[i] - out[i], 0);
}
if (out[i]>in[i]){//出度大于入度
addEdge(n, i, out[i] - in[i], 0);
}
}
}
bool BellmanFloyd(int start,int end,double& flow,double& cost){
int inq[110];
double price[110];
int Flow[110];
int parent[110];
Flow[start] = Inf;
for (int i = 0; i < 110; i++) price[i] = 1000000;
price[start] = 0;
memset(inq, 0, sizeof(inq));
inq[start] = 1;
int front = 0, tail = 1;
q[front] = start;
while (front<tail){
int id = q[front++];
inq[id] = 0;
for (int i = G[id]; i !=-1; i=edge[i].next){
int to = edge[i].to;
if (edge[i].cap > edge[i].flow&&price[to] > price[id] + edge[i].cost){
Flow[to] = min(Flow[id], edge[i].cap - edge[i].flow);
price[to] = price[id] + edge[i].cost;
parent[to] = i;
if (!inq[to]){
q[tail++]=to;
inq[to] = 1;
}
}
}
}
if (price[end] == 1000000) return false;
flow += Flow[end];
cost += Flow[end]*price[end];
for (int i = end; i != start; i = edge[parent[i]].from){
edge[parent[i]].flow += Flow[end];
edge[parent[i] ^ 1].flow -= Flow[end];
}
return true;
}
double MaxFlow(int start,int end){
double flow = 0,cost = 0;
while (BellmanFloyd(start, end, flow, cost));
return cost;
}
double Deal(){
Init();
double res=MaxFlow(n, n + 1);
return -(sum + res)+eps;
}
};
int main(){
Solve a;
int Case = 0;
while (scanf("%d",&a.n)){
Case++;
if (a.n == 0) break;
double res= a.Deal();
printf("Case %d: %.2lf\n", Case,res);
}
return 0;
}