题意:N个人要回答M道题。给出每个人回答每道题成功的概率。同时有限制:任何时刻,任意两个人回答问题的个数的差不能超过一个。求这些人回答完这些问题后,期望的回答正确的个数。
思路:因为任何时刻,任意两个人的回答问题的个数不能超过一个。这样其实是把这M个问题分组了。每一组有N个问题,在这N个问题中,怎样分配答题方案使期望的回答正确的个数最多。
这样就转化成了指派问题,可以用费用流求解。
代码如下:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const double EPS = 1e-6;
const double INF = 0x3f3f3f3f;//无穷大值
int dcmp(double x)
{
if(fabs(x) < EPS) return 0;
else if(x > 0) return 1;
else return -1;
}
struct edge
{
int from, to, cap, flow;
double cost;
edge(int u = 0, int v = 0, int c = 0, int f = 0, double w = 0) :
from(u), to(v), cap(c), flow(f), cost(w) {}
};
struct mcmf
{
static const int MAX = 300;//点的数目
edge edges[2 * MAX];//储存边的数组
int head[MAX];//每个节点对应链表的开始位置
int next[MAX];//链表的下一个节点在edges数组的位置
int tot;//edges数组的大小
int que[MAX], front, tail;//spfa用队列
bool inq[MAX];//spfa入队标记
double dis[MAX];//spfa距离
int p[MAX];//增广路中,节点前面的一个弧的标号
int a[MAX];//可行的增广流
int src, sink;//src源点,sink汇点
int flow;
double cost;//求出的最大流,最小花费
void init()
{
memset(head, -1, sizeof(head));
tot = 0;
}
void addedge(int from, int to, int cap, double cost)
{
edges[tot] = edge(from, to, cap, 0, cost);
next[tot] = head[from], head[from] = tot++;
edges[tot] = edge(to, from, 0, 0, -cost);
next[tot] = head[to], head[to] = tot++;
}
bool spfa(int & flow, double &cost)
{
fill(dis,dis+MAX,INF);
memset(inq, false, sizeof(inq));
dis[src] = 0.0, inq[src] = true, p[src] = 0, a[src] = INF;
front = tail = 0;
que[tail++] = src;
while (front < tail)
{
int u = que[front++];
inq[u] = false;
for (int v = head[u]; v != -1; v = next[v])
{
edge & e = edges[v];
if (e.cap > e.flow && dcmp(dis[e.to] - dis[u] - e.cost )> 0)
{
dis[e.to] = dis[u] + e.cost;
p[e.to] = v;
a[e.to] = min(a[u], e.cap - e.flow);
if (!inq[e.to])
que[tail++] = e.to, inq[e.to] = true;
}
}
}
if (dcmp(dis[sink] -INF) == 0) return false;
flow += a[sink];
cost += dis[sink];//单位费用,整体费用
for (int u = sink; u != src; u = edges[p[u]].from)
{
edges[p[u]].flow += a[sink];
edges[p[u] ^ 1].flow -= a[sink];
}
return true;
}
double mincost(int s, int t)
{
src = s, sink = t;
flow = 0;
cost = 0;
while (spfa(flow, cost));
return cost;
}
} solver;
int t;
int N,M;
double map[15][1005];
double ans=0;
int main()
{
//freopen("input.txt","r",stdin);
scanf("%d",&t);
int ca;
for(ca=1; ca<=t; ca++)
{
scanf("%d %d",&N,&M);
int i,j;
ans=0;
for(i=1; i<=N; i++)
for(j=1; j<=M; j++)
{
scanf("%lf",&map[i][j]);
}
int num;
int ii,jj;
for(num=0; num<M/N; num++)
{
solver.init();
for(ii=1; ii<=N; ii++)
{
solver.addedge(0,ii,1,0);
for(jj=1+N; jj<=N+N; jj++)
solver.addedge(ii,jj,1,-map[ii][jj+num*N-N]);
}
for(jj=1+N; jj<=N+N; jj++)
solver.addedge(jj,2*N+1,1,0);
ans-=solver.mincost(0,2*N+1);
}
if(M%N!=0)
{
solver.init();
for(ii=1; ii<=N; ii++)
{
solver.addedge(0,ii,1,0);
for(jj=1+N; jj<=M%N+N; jj++)
solver.addedge(ii,jj,1,-map[ii][jj+M/N*N-N]);
}
for(jj=1+N; jj<=M%N+N; jj++)
solver.addedge(jj,M%N+N+1,1,0);
ans-=solver.mincost(0,M%N+N+1);
}
printf("Case #%d: %.5f\n",ca,ans);
}
return 0;
}