Birthday 经过次数不同费用不同的费用流
题目
Birthday(https://ac.nowcoder.com/acm/problem/19802)
题目描述
恬恬的生日临近了。宇扬给她准备了一个蛋糕。
正如往常一样,宇扬在蛋糕上插了n支蜡烛,并把蛋糕分为m个区域。因为某种原因,他必须把第i根蜡烛插在第ai个区域或第bi个区域。区域之间是不相交的。宇扬在一个区域内同时摆放x支蜡烛就要花费x2的时间。宇扬布置蛋糕所用的总时间是他在每个区域花的时间的和。
宇扬想快些见到恬恬,你能告诉他布置蛋糕最少需要多少时间吗?
输入描述:
第一行包含两个整数n,m(1 ≤ n ≤ 50, 2≤ m≤ 50)。
接下来n行,每行两个整数ai,bi(1 ≤ ai, bi ≤ m)。
输出描述:
一个整数表示答案。
样例一
输入
3 3
1 2
1 2
1 2
输出
5
样例二
输入
3 3
1 2
2 3
1 3
输出
3
思路
把蜡烛和区域当作点。那么就是一个最小费用最大流。多少每个区域经过的次数不同,费用不同。最多一个区域可能放n个点。那么把一个点拆成n个点。分别表示经过的次数。第1个点为1,那么第22-1=3,第3个点费用为33-3=6,....。每个次数只能流量为1。
#pragma GCC optimize(3, "Ofast", "inline")
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL maxn=3010;
const LL maxm=1e6+10;
struct Mcmf_flow{
bool vis[maxn];
LL dis[maxn];
LL pre[maxn];
LL last[maxn];
LL flow[maxn];
LL zdl, fy;
LL s1[maxn];//sum的前缀和
LL sum[maxn];//流量为i的最小费用
LL tot=0;
//dis最小花费;pre每个点的前驱;last每个点的所连的前一条边;flow源点到此处的流量
struct Edge {
LL to,next,flow,dis;//flow流量 dis花费
} e[maxm<<1];
LL head[maxn],cut;
queue <LL> q;
int N=0;
void init(int n){
N=n+5;
memset(s1, 0, sizeof(LL)*(N+5));
memset(sum, 0, sizeof(LL)*(N+5));
memset(head,-1,sizeof(LL)*(N+5));
tot=0;
cut=-1;//初始化
zdl=fy=0;
}
void add_e(LL from,LL to,LL flow,LL dis) {
e[++cut].next=head[from];
e[cut].to=to;
e[cut].flow=flow;
e[cut].dis=dis;
head[from]=cut;
}
void add(LL x, LL y, LL z, LL f){
//cout<<x<<" "<<y<<" "<<f<<endl;
add_e(x,y,z,f);
add_e(y,x,0,-f);
}
bool spfa(LL s,LL t) {
memset(dis,0x7f,sizeof(LL)*(N+5));
memset(flow,0x7f,sizeof(LL)*(N+5));
memset(vis,0,sizeof(bool)*(N+5));
q.push(s);
vis[s]=1; dis[s]=0; pre[t]=-1;
while (!q.empty()) {
LL now=q.front();
q.pop(); vis[now]=0;
for (LL i=head[now]; i!=-1; i=e[i].next) {
if (e[i].flow>0 && dis[e[i].to]>dis[now]+e[i].dis) { //正边
dis[e[i].to]=dis[now]+e[i].dis;
pre[e[i].to]=now;
last[e[i].to]=i;
flow[e[i].to]=min(flow[now],e[i].flow);//
if (!vis[e[i].to]) {
vis[e[i].to]=1;
q.push(e[i].to);
}
}
}
}
return pre[t]!=-1;
}
void MCMF(LL s, LL t) {
while (spfa(s,t)) { //+1
LL now=t;
zdl+=flow[t];
fy+=flow[t]*dis[t];
sum[++tot]=fy;//得到sum[]
s1[tot]=sum[tot]-sum[tot-1];//得到s1[]
while (now!=s) {
//从源点一直回溯到汇点
e[last[now]].flow-=flow[t];//flow和dis容易搞混
e[last[now]^1].flow+=flow[t];
now=pre[now];
}
}
}
}min_Flow;
int a[55], b[55];
int n, m;
int id(int x, int cut){
return (cut)*n+x;
}
int w[55];
int main() {
for(int i=1; i<=50; i++){
w[i]=i*i-(i-1)*(i-1);
}
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++){
scanf("%d%d", &a[i], &b[i]);
}
int S=0, T=3000;
min_Flow.init(T);
for(int i=1; i<=n; i++){
min_Flow.add(S, i, 1, 0);
for(int k=1; k<=n; k++){
min_Flow.add(i, id(a[i], k), 1, w[k]);
min_Flow.add(i, id(b[i], k), 1, w[k]);
}
}
for(int i=1; i<=m; i++){
for(int k=1; k<=n; k++){
min_Flow.add(id(i, k), T, 1, 0);
}
}
min_Flow.MCMF(S, T);
printf("%lld\n", min_Flow.fy);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)