P3749 题解
P3749 [六省联考 2017] 寿司餐厅 题解
发现很少有人讲为什么这题是最大权闭合子图,但作为一个刚学网络流的蒟蒻,我认为考虑是必要的。
最大权闭合子图的特点:
- 存在单向依赖关系,选
必须选 。 - 每个点只会被选一次。
- 代价有正有负。
本问题特点:
- 选一个区间,必选所有子区间(单向依赖)。
- 贡献 & 代价都只算一次。
- 有正有负。
完美符合要求!
只需要打个补丁,选
会最大权闭合子图的可以跳过下面一部分。
最大权闭合子图模型
考虑最优为选所有正的,不选负的。
建图:
为源点, 为汇点。- 对于依赖关系
,连边 。 - 对于节点,若代价为正,连边
;反之,连边 。
若割掉与
发现
- 如果保留
,即选 ,则必然割掉 ,即不选 。 - 如果割掉
,即不选 , 可割可不割,不受影响。
故此图的割满足选
最大权为 所有正权值之和
讲到这里本题建图就很简单了,不懂的请自行看代码理解。
#include<bits/stdc++.h> using namespace std; #define rep(i,x,y) for(int i=x;i<=y;i++) #define REPG(i,g,x) for(int i=g.head[x];~i;i=g.edge[i].nxt) //此处省略快读板子 const int N=1e5+5,M=2e6+5; struct graph{ int head[N],cnt; struct node{ int to,nxt,w,c; }edge[M]; inline void add(int x,int y,int w,int c){ edge[++cnt]=(node){y,head[x],w,c};head[x]=cnt; } inline void clear(){ memset(head,-1,sizeof(head));cnt=1; } }g; int n,m,s,t; const int NF=105; const int INF=1e9+5; int a[NF],d[NF][NF],id[NF][NF]; int Maxa; namespace Dinic{ int dep[N],cur[N]; queue<int> q; bool bfs(){ memset(dep,-1,sizeof(dep)); memcpy(cur,g.head,sizeof(g.head)); while(!q.empty()) q.pop(); dep[s]=1;q.push(s); while(!q.empty()){ int u=q.front();q.pop(); if(u==t) break; REPG(i,g,u){ int v=g.edge[i].to,w=g.edge[i].w,c=g.edge[i].c; if(dep[v]==-1 && w>c){ dep[v]=dep[u]+1; q.push(v); } } } return dep[t]!=-1; } int dfs(int u,int f){ if(u==t || !f) return f; int res=0; for(int i=cur[u];(~i) && f!=res;i=g.edge[i].nxt){ int v=g.edge[i].to,w=g.edge[i].w,c=g.edge[i].c; cur[u]=i; if(dep[v]==dep[u]+1 && w>c){ int nf=dfs(v,min(f-res,w-c)); res+=nf; g.edge[i].c+=nf; g.edge[i^1].c-=nf; } } return res; } int solve(){ int ans=0; while(bfs()) ans+=dfs(s,INF); return ans; } } int main(){ g.clear(); read(n),read(m); rep(i,1,n) read(a[i]),Maxa=max(Maxa,a[i]); s=1,t=2; int tot=2; rep(i,1,n) rep(j,i,n){ read(d[i][j]); id[i][j]=++tot; } int mx=0; rep(i,1,n) rep(j,i,n){ if(i==j){ //选 d[i][i] 必选 a[i] g.add(id[i][j],tot+a[i],INF,0); g.add(tot+a[i],id[i][j],0,0); //选 d[i][i] 需要付出 a[i] 的代价 d[i][j]-=a[i]; } else{ //选 d[i][j] 必选 d[i+1][j],d[i][j-1] g.add(id[i][j],id[i+1][j],INF,0); g.add(id[i+1][j],id[i][j],0,0); g.add(id[i][j],id[i][j-1],INF,0); g.add(id[i][j-1],id[i][j],0,0); } if(d[i][j]>0) g.add(s,id[i][j],d[i][j],0),g.add(id[i][j],s,0,0),mx+=d[i][j]; else g.add(id[i][j],t,-d[i][j],0),g.add(t,id[i][j],0,0); } //选 i 需要付出 m*i*i 的代价 rep(i,1,Maxa) g.add(tot+i,t,m*i*i,0),g.add(t,tot+i,0,0); printf("%d\n",mx-Dinic::solve()); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】