Problem Description
Little Q’s factory recently purchased m pieces of new equipment, labeled by 1,2,…,m.
There are n workers in the factory, labeled by 1,2,…,n. Each worker can be assigned to no more than one piece of equipment, and no piece of equipment can be assigned to multiple workers. If Little Q assigns the i-th worker to the j-th piece of equipment, he will need to pay ai×j2+bi×j+ci dollars.
Now please for every k (1≤k≤n) find k pairs of workers and pieces of equipment, then assign workers to these pieces of equipment, such that the total cost for these k workers is minimized.
Input
The first line of the input contains a single integer T (1≤T≤10), the number of test cases.
For each case, the first line of the input contains two integers n and m (1≤n≤50, n≤m≤108), denoting the number of workers and the number of pieces of equipment.
Each of the following n lines contains three integers ai,bi and ci (1≤ai≤10, −108≤bi≤108, 0≤ci≤1016, b2i≤4aici), denoting a worker.
Output
For each test case, output a single line containing n integers, the k-th (1≤k≤n) of which denoting the minimum possible total cost for k pairs of workers and pieces of equipment.
Sample Input
1
3 5
2 3 10
2 -3 10
1 -1 4
Sample Output
4 15 37
Source
2020 Multi-University Training Contest 2
仍然是个网络流小白,嘤嘤嘤。
题意:
n个人,m个机器。每个人有三个属性a,b,c。每个人要找一个机器,每个机器至多分配给一个人。第 i i i个人选了第 j j j个机器,则花费为 a [ i ] ∗ x 2 + b [ i ] ∗ x + c [ i ] a[i]*x^2+b[i]*x+c[i] a[i]∗x2+b[i]∗x+c[i]。
求最小花费。
思路:
参考了hl大佬的博客:https://www.cnblogs.com/lonely-wind-/p/13368156.html
这是一个二次函数,而且是个凹函数(a>0)。
那么我们对每个人,直接三分找对应机器的最小值,然后从这个最小值点,往左右延伸 n − 1 n-1 n−1个点,得到 n n n个预选点,然后向这个 n n n个点建一条流量为1,费用为对应花费的边。也就是人向机器连边。
最后确定一个源点,向所有人连边,确定一个汇点,将所有机器向汇点连边。
跑一个最小费用最大流即可。
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N = 5005, M = 200010;
ll ver[M], edge[M], cost[M], Next[M], head[N];
ll d[N], incf[N], pre[N], v[N];
ll n, k, tot, s, t, maxflow, ans;
void add(int x, int y, int z, ll c) {
// 正向边,初始容量z,单位费用c
ver[++tot] = y, edge[tot] = z, cost[tot] = c;
Next[tot] = head[x], head[x] = tot;
// 反向边,初始容量0,单位费用-c,与正向边“成对存储”
ver[++tot] = x, edge[tot] = 0, cost[tot] = -c;
Next[tot] = head[y], head[y] = tot;
}
bool spfa() {
queue<int> q;
memset(d, 0x3f, sizeof(d)); // -INF
memset(v, 0, sizeof(v));
q.push(s); d[s] = 0; v[s] = 1; // SPFA 求最长路
incf[s] = 1ll << 62; // 增广路上各边的最小剩余容量
while (q.size()) {
int x = q.front(); v[x] = 0; q.pop();
for (int i = head[x]; i; i = Next[i]) {
if (!edge[i]) continue; // 剩余容量为0,不在残量网络中,不遍历
int y = ver[i];
if (d[y]>d[x] + cost[i]) {
d[y] = d[x] + cost[i];
incf[y] = min(incf[x], edge[i]);
pre[y] = i; // 记录前驱,便于找到最长路的实际方案
if (!v[y]) v[y] = 1, q.push(y);
}
}
}
if (d[t] == 0x3f3f3f3f3f3f3f3f) return false; // 汇点不可达,已求出最大流
return true;
}
// 更新最长增广路及其反向边的剩余容量
void update() {
int x = t;
while (x != s) {
int i = pre[x];
edge[i] -= incf[t];
edge[i ^ 1] += incf[t]; // 利用“成对存储”的xor 1技巧
x = ver[i ^ 1];
}
maxflow += incf[t];
ans += d[t] * incf[t];
}
int match[55][55];
ll a[55],b[55],c[55];
void init() {
memset(head,0,sizeof(head));
memset(pre,0,sizeof(pre));
memset(incf,0,sizeof(incf));
tot = 1;
ans = 0;
}
ll f(int i,ll x) {
return a[i] * x * x + b[i] * x + c[i];
}
int main() {
int T;scanf("%d",&T);
while(T--) {
scanf("%d%d",&n,&k);
init();
for(int i = 1;i <= n;i++) {
scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
int l = 1,r = k;
int pos = 1;
while(l < r) {
int m1 = (2 * l + r) / 3;
int m2 = (l + 2 * r + 2) / 3;
if(f(i,m1) > f(i,m2)) {
l = m1 + 1;
} else {
r = m2 - 1;
}
}
if(f(i,l) < f(i,r)) {
pos = l;
} else {
pos = r;
}
l = pos - 1,r = pos + 1;
match[i][1] = pos;
for(int j = 2;j <= n;j++) {
if(f(i,l) < f(i,r)) {
if(l >= 1 && l <= k) {
match[i][j] =l;
l--;
} else {
match[i][j] = r;
r++;
}
} else {
if(r >= 1 && r <= k) {
match[i][j] = r;
r++;
} else {
match[i][j] = l;
l--;
}
}
}
}
unordered_map<int,int>mp;
int cnt = 0;
for(int i = 1;i <= n;i++) {
for(int j = 1;j <= n;j++) {
if(!mp[match[i][j]]) {
mp[match[i][j]] = ++cnt;
}
}
}
s = 0,t = n + cnt + 1;
for(int i = 1;i <= n;i++) {
add(s,i + cnt,1,0);
for(int j = 1;j <= n;j++) {
add(i + cnt,mp[match[i][j]],1,f(i,match[i][j]));
}
}
for(int i = 1;i <= cnt;i++) {
add(i,t,1,0);
}
for(int i = 1;i <= n;i++) {
spfa();
update();
printf("%lld",ans);
printf("%c",i == n ? '\n' : ' ');
}
}
return 0;
}