P3236 [HNOI2014]画框
与最小乘积生成树类似。
考虑维护左下凸壳上的点,每次重设边权然后求最小匹配。
具体做法和上面的链接类似,只不过最小生成树部分变成了最小匹配。
代码使用费用流:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
#define ll long long
const int N = 155 * 155, M = 1005;
int e[N], h[N], ne[N], lim[N], cost[N], idx;
int a[M][M], b[M][M];
int n, t, S, T;
struct Point
{
int x, y;
Point(int _x, int _y) : x(_x), y(_y) {}
Point()
{
x = y = 0;
}
Point operator-(const Point& xx) const
{
return Point(x - xx.x, y - xx.y);
}
int operator*(const Point& xx) const
{
return x * xx.y - y * xx.x;
}
};
Point res = Point((int)1e9, (int)1e9);
void add(int u, int v, int w, int c)
{
e[idx] = v, cost[idx] = c, lim[idx] = w, ne[idx] = h[u], h[u] = idx++;
e[idx] = u, cost[idx] = -c, lim[idx] = 0, ne[idx] = h[v], h[v] = idx++;
}
int cur[N], dist[N];
bool isin[N];
int spfa()
{
for (int i = 0; i <= T; i++) dist[i] = (int)1e9, isin[i] = 0;
isin[S] = 1;
cur[S] = h[S];
queue<int> q;
q.push(S);
dist[S] = 0;
while (q.size())
{
int u = q.front();
q.pop();
isin[u] = 0;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
//printf("%lld %lld\n", u, dist[j]);
if (lim[i] > 0 && dist[j] > dist[u] + cost[i])
{
dist[j] = dist[u] + cost[i];
if (!isin[j])
{
isin[j] = 1;
q.push(j);
}
cur[j] = h[j];
}
}
}
//system("pause");
return dist[T] != (int)1e9;
}
inline int dfs(int u, int lm)
{
if (u == T) return lm;
isin[u] = 1;
int flow = 0;
for (int i = cur[u]; ~i && flow < lm; i = ne[i])
{
cur[u] = i;
int j = e[i];
if (lim[i] > 0 && !isin[j] && dist[j] == dist[u] + cost[i])
{
//printf("%d\n", j);
int k = dfs(j, min(lim[i], lm - flow));
flow += k;
lim[i] -= k;
lim[i ^ 1] += k;
}
}
isin[u] = 0;
return flow;
}
int IDX[M][M];
inline Point dinic()
{
while (spfa())
{
//printf("oh\n");
int u;
while (u = dfs(S, (int)1e9));
}
Point ret = Point(0, 0);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
int p = IDX[i][j + n];
if (lim[p] == 0)
{
ret.x += a[i][j];
ret.y += b[i][j];
}
}
}
if (1LL * ret.x * ret.y < 1LL * res.x * res.y)
{
res = ret;
}
return ret;
}
void solve(Point A, Point B)
{
//printf("oh\n");
for (int i = 0; i < N; i++) h[i] = -1, ne[i] = 0;
idx = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
IDX[i][j + n] = idx;
add(i, j + n, 1, (B.x - A.x) * b[i][j] + (A.y - B.y) * a[i][j]);
}
}
for (int i = 1; i <= n; i++)
{
add(S, i, 1, 0);
add(i + n, T, 1, 0);
}
Point C = dinic();
if ((B - A) * (C - A) >= 0) return;
solve(A, C);
solve(C, B);
}
signed main()
{
scanf("%lld", &t);
while (t--)
{
res = Point((int)1e9, (int)1e9);
scanf("%d", &n);
S = 0, T = n + n + 1;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++) scanf("%d", &a[i][j]);
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++) scanf("%d", &b[i][j]);
}
Point A, B;
for (int i = 0; i < N; i++) h[i] = -1, ne[i] = 0;
idx = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
IDX[i][j + n] = idx;
add(i, j + n, 1, a[i][j]);
}
}
for (int i = 1; i <= n; i++)
{
add(S, i, 1, 0);
add(i + n, T, 1, 0);
}
A = dinic();
for (int i = 0; i < N; i++) h[i] = -1, ne[i] = 0;
idx = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
IDX[i][j + n] = idx;
add(i, j + n, 1, b[i][j]);
}
}
for (int i = 1; i <= n; i++)
{
add(S, i, 1, 0);
add(i + n, T, 1, 0);
}
B = dinic();
solve(A, B);
printf("%lld\n", 1LL * res.x * res.y);
}
return 0;
}
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现