金华网络赛1007题,拆边费用流。n天,m门课程,添加源汇s、t后很容易构图:s -> n个点连接流量为k,费用为0的边,n个点分别到m门课程根据01矩阵确定是否连边,每条边依然是流量为k,费用为0。最后就是m门课程到汇点t,如果课程基础分数base[i] < 60,则先连接一条边流量为60 - base[i],然后连接40条边(61 -- 100),每条边流量为1,费用为F(i) - F(i-1);否则,连接一些边(base[i]+1 -- 100),每条边流量为1,费用同样为F(i) - F(i-1),这里的拆边是重点。
这种是我在比赛时的构图方法,但一直TLE,优化了很多地方始终都不能过!其实上面的构图方法是完全正确的,是 s -> 天数 -> 课程数 -> t 的模型,结果却是TLE,而只要将模型变为 s -> 课程数 -> 天数 -> t,结果是46ms AC!如果这是有专门数据卡构图方式,只能说出题者太高明了。。。。。
代码比赛时写的,只是后来换了构图模型,比较凌乱。
/****************************************************************
Problem: HDU 4406
User: Jeflie
Language: C++
Result: Accepted
Time: 46 ms
Memory: 336 kb
****************************************************************/
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int INF = 1e9;
const int N = 100;
struct Data
{
int x, y, f, next;
double w;
} edge[N*N];
int inx;
int node[N], path[N];
void addedge(int u, int v, int f, double w)
{
//cout<<u<<" "<<v<<" "<<f<<" "<<w<<endl;
edge[inx].x = u, edge[inx].y = v, edge[inx].f = f, edge[inx].w = w;
edge[inx].next = node[u], node[u] = inx++;
edge[inx].x = v, edge[inx].y = u, edge[inx].f = 0, edge[inx].w = -w;
edge[inx].next = node[v], node[v] = inx++;
}
double dist[N];
int mark[N], que[N];
bool SPFA(int s, int e)
{
for (int i = 0; i < N; i++)
dist[i] = -1.0 * INF;
memset(mark, 0, sizeof(mark));
que[0] = s;
int head = 1, tail = 0;
dist[s] = 0;
path[s] = -1;
while ( head != tail )
{
int u = que[tail++];
if (tail == N) tail = 0;
mark[u] = 0;
for (int ip = node[u]; ip != -1; ip = edge[ip].next)
{
Data &v = edge[ip];
if (v.f > 0 && dist[u] + v.w > dist[v.y])
{
dist[v.y] = dist[u] + v.w;
path[v.y] = ip;
if (mark[v.y] == 0)
{
mark[v.y] = 1;
que[head++] = v.y;
if (head == N) head = 0;
}
}
}
}
return (dist[e] > 0);
}
void Fun(int s, int t)
{
while ( SPFA(s, t) )
{
int Min = INF;
for (int i = path[t]; i != -1; i = path[edge[i].x])
Min = min(Min, edge[i].f);
for (int i = path[t]; i != -1; i = path[edge[i].x])
{
edge[i].f -= Min;
edge[i^1].f += Min;
}
}
}
int W[100], base[100];
int course[100][100];
int main()
{
int n, k, m;
while (scanf("%d %d %d", &n, &k, &m) != EOF)
{
if (n + k + m == 0) break;
inx = 0;
memset(node, -1, sizeof(node));
for (int i = 1; i <= m; i++)
scanf("%d", &W[i]);
for (int i = 1; i <= m; i++)
scanf("%d", &base[i]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &course[i][j]);
int s = 98, t = 99;
for (int i = 1; i <= n; i++)
addedge(i, t, k, 0);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
if (course[i][j])
addedge(50+j, i, k, 0);
}
for (int i = 1; i <= m; i++)
{
double pre = 0;
if (base[i] < 60)
{
addedge(s, 50+i, 60-base[i], 1.0*INF);
pre = (4.0 - (3.0 * ((100-60)*(100-60))/1600)) * W[i];
for (int j = 61; j <= 100; j++)
{
double tmp = (4.0 - (3.0 * ((100-j)*(100-j))/1600)) * W[i];
addedge(s, 50+i, 1, tmp - pre);
pre = tmp;
}
}
else
{
pre = (4.0 - (3.0 * ((100-base[i])*(100-base[i]))/1600)) * W[i];
for (int j = base[i] + 1; j <= 100; j++)
{
double tmp = (4.0 - (3.0 * ((100-j)*(100-j))/1600)) * W[i];
addedge(s, 50+i, 1, tmp - pre);
pre = tmp;
}
}
}
Fun(s, t);
double ans = 0;
int flag = 0;
int add[100];
memset(add, 0, sizeof(add));
for (int ip = node[s]; ip != -1; ip = edge[ip].next)
add[edge[ip].y-50] += edge[ip^1].f;
int temp = 0;
for (int i = 1; i <= m; i++)
{
temp += W[i];
if (base[i] + add[i] < 60) flag = 1;
ans += (4.0 - 3.0 * ((100-base[i]-add[i])*(100-base[i]-add[i])) / 1600) * W[i];
//cout<<i<<" "<<base[i]<<" "<<add[i]<<endl;
}
ans /= temp;
if (flag)
printf("0.000000\n");
else
printf("%.6lf\n", ans);
}
return 0;
}