题解 一般图带权多重匹配
看起来是个费用流题,但是图不知道该怎么建
发现权值为奇数的点数很少
先考虑没有权值为奇数的点时怎么做
(并不知道怎么想到)
考虑将最优决策下的一个操作 视作一条无向边
那么存在一种给边定向的方法使得每个点的入度等于出度(欧拉回路)
所以我们可以将每个点拆成一个左部点和一个右部点,流量各为
这样就可以费用流了
然后考虑有权为奇的点怎么做
直接状压额外的1流量在左部还是右部即可
要求在左部和右部的额外流量相等
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 110
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, k;
int a[N], c[N][N];
namespace force{
int bit[N];
unordered_map<int, int> mp;
int dfs(int s) {
if (!s) return 0;
if (mp.find(s)!=mp.end()) return mp[s];
int ans=INF;
for (int i=1; i<=n; ++i) {
for (int j=i; j<=n; ++j) {
if (i!=j) {
int t1=(s>>bit[i])&3, t2=(s>>bit[j])&3;
if (t1 && t2) ans=min(ans, dfs(s-(t1<<bit[i])-(t2<<bit[j])+((t1-1)<<bit[i])+((t2-1)<<bit[j]))+c[i][j]);
}
else {
int t=(s>>bit[i])&3;
if (t>=2) ans=min(ans, dfs(s-(t<<bit[i])+((t-2)<<bit[i]))+c[i][i]);
}
}
}
return mp[s]=ans;
}
void solve() {
for (int i=1; i<=n; ++i) bit[i]=i<<1;
int s=0;
for (int i=1; i<=n; ++i) s+=a[i]<<bit[i];
printf("%d\n", dfs(s));
}
}
namespace task{
bool vis[N];
int dis[N], back[N], inc[N];
int head[N], l[N], r[N], sta[N], ecnt, top, s, t, ans=INF;
struct edge{int to, next, flw, cst;}e[N*N*2];
inline void add(int s, int t, int f, int w) {e[++ecnt]={t, head[s], f, w}; head[s]=ecnt;}
bool spfa() {
memset(dis, 127, sizeof(dis));
memset(back, -1, sizeof(back));
dis[s]=0; inc[s]=INF;
queue<int> q;
q.push(s);
int u;
while (q.size()) {
u=q.front(); q.pop();
vis[u]=0;
for (int i=head[u],v; ~i; i=e[i].next) {
v=e[i].to;
if (e[i].flw && dis[u]+e[i].cst<dis[v]) {
dis[v]=dis[u]+e[i].cst;
back[v]=i; inc[v]=min(inc[u], e[i].flw);
if (!vis[v]) q.push(v), vis[v]=1;
}
}
}
return ~back[t];
}
int dinic() {
int ans=0;
while (spfa()) {
ans+=dis[t]*inc[t];
for (int u=t; u!=s; u=e[back[u]^1].to) {
e[back[u]].flw-=inc[t];
e[back[u]^1].flw+=inc[t];
}
}
return ans;
}
void solve() {
for (int i=1; i<=n; ++i) if (a[i]&1) sta[++top]=i;
int lim=1<<top;
s=n*2+1, t=n*2+2;
for (int s=0; s<lim; ++s) {
if (__builtin_popcount(s)!=top/2) continue;
for (int i=1; i<=n*2+2; ++i) head[i]=-1; ecnt=1;
for (int i=1; i<=n; ++i) l[i]=r[i]=a[i]/2;
for (int i=1; i<=top; ++i) ++((s&(1<<(i-1)))?l[sta[i]]:r[sta[i]]);
// cout<<"l: "; for (int i=1; i<=n; ++i) cout<<l[i]<<' '; cout<<endl;
// cout<<"r: "; for (int i=1; i<=n; ++i) cout<<r[i]<<' '; cout<<endl;
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j)
add(i, j+n, INF, c[i][j]), add(j+n, i, 0, -c[i][j]);
for (int i=1; i<=n; ++i) {
add(task::s, i, l[i], 0), add(i, task::s, 0, 0);
add(i+n, t, r[i], 0), add(t, i+n, 0, 0);
}
ans=min(ans, dinic());
}
printf("%d\n", ans);
}
}
signed main()
{
freopen("match.in", "r", stdin);
freopen("match.out", "w", stdout);
n=read();
for (int i=1; i<=n; ++i) k+=(a[i]=read())&1;
if (k&1) {puts("-1"); return 0;}
for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) c[i][j]=read();
// force::solve();
task::solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现