很久以前,当whalyzh同学是一个萌新的时候,遇到了这么一个问题:
给定长为的序列,构造一个只有0和1的长为的序列,使得的值最大。
小Q同学想了一秒钟之后说:这不是一眼题么?然后whalyzh同学瞬间就会了。
过了几天,当whalyzh同学还是一个萌新的时候,遇到了这么一个问题:
给定阶方阵,构造一个只有0和1的的向量,使得的值最大。
小Q同学想了一分钟之后说:这不是一眼题么?然后whalyzh同学瞬间就会了。
又过了几天,当whalyzh仍然是一个萌新的时候,遇到了这么一个问题:
给定阶方阵,构造一个只有0和1的的向量,使得的值最大。
小Q同学想了一小时之后说:不能总是问我,你得自己去思考。然后whalyzh同学瞬间就懵逼了。
即使到现在,whalyzh同学仍然百思不得其解,希望你能帮他解决这个问题。
请注意,在这个问题中,规定。
Input
第一行是一个正整数,表示测试数据的组数,
对于每组测试数据,
第一行是一个整数,表示方阵的大小,
接下来行,每行个整数,表示方阵中的数。
Output
对于每组测试数据,输出所求的最大值,答案精确到小数点后五位。
Sample Input
1 3 0 1 0 1 2 4 1 3 2
Sample Output
3.50000
Hint
对于样例,令,可取得最大值。
Source
Author
题目大意:
给出一个矩阵B(元素小于等于10)和一个由01组成的向量A,能选择其中的B[i][j]当且仅当A[i]=A[j]=1且i!=j,给定B通过A中把一些数置为1来选择B中的数使得B中数的和比上A中1的个数最大。
解题思路:
原问题等价于给定一个n个顶点的图,每两点之间有一条边,边权为1到10,然后从中选出一些点,使的这些点的诱导子图的边权和/点数尽可能大,这是一个最大密度子图问题。
感谢chitanda的代码~
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 100
const double eps = 1e-8;
const double inf = 1e+8;
struct Edge { int v; double w; Edge *x; } *e, *lnk[N * N + 9], E[12 * N * N + 9];
int TT, S, T, n, m, b[N + 9][N + 9];
int g[N * N + 9], h[N * N + 9];
int dcmp(double x) {
return (x > +eps) - (x < -eps);
}
void add(int u, int v, double w) {
e->v = v, e->w = w, e->x = lnk[u], lnk[u] = e++;
e->v = u, e->w = 0, e->x = lnk[v], lnk[v] = e++;
}
double sap(int u, double flw) {
if (u == T) return flw;
double det, sum = .0;
for (Edge *p = lnk[u]; p; p = p->x)
if (h[u] == h[p->v] + 1 && dcmp(p->w)) {
det = sap(p->v, min(p->w, flw - sum));
p->w -= det;
E[(p - E) ^ 1].w += det;
if (dcmp((sum += det) - flw) == 0) return sum;
}
if (h[S] >= m) return sum;
if (--g[h[u]] == 0) h[S] = m;
++g[++h[u]];
return sum;
}
bool check(double x) {
m = n + n * (n - 1) / 2;
S = ++m, T = ++m;
e = E;
for (int i = 1; i <= m; ++i) lnk[i] = 0;
for (int i = 1; i <= n; ++i) add(i, T, x);
int cnt = n;
double sum = .0;
for (int i = 1; i < n; ++i)
for (int j = i + 1; j <= n; ++j) {
++cnt;
if (b[i][j]) {
sum += b[i][j];
add(S, cnt, b[i][j]);
}
add(cnt, i, inf);
add(cnt, j, inf);
}
g[0] = m;
for (int i = 1; i <= m; ++i) g[i] = h[i] = 0;
while (h[S] < m) sum -= sap(S, inf);
return sum > eps;
}
int main() {
scanf("%d", &TT);
while (TT--) {
scanf("%d", &n);
memset(b, 0, sizeof b);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
{
int x;
scanf("%d", &x);
if (i < j) b[i][j] += x;
if (i > j) b[j][i] += x;
}
double l = 0, r = 1000, m;
while (fabs(r - l) > eps)
{
m = (l + r) / 2;
if (check(m)) l = m;
else r = m;
}
printf("%.5f\n", l);
}
return 0;
}