P3749 题解

P3749 [六省联考 2017] 寿司餐厅 题解

发现很少有人讲为什么这题是最大权闭合子图,但作为一个刚学网络流的蒟蒻,我认为考虑是必要的。

最大权闭合子图的特点:

  • 存在单向依赖关系,选 x 必须选 y
  • 每个点只会被选一次。
  • 代价有正有负。

本问题特点:

  • 选一个区间,必选所有子区间(单向依赖)。
  • 贡献 & 代价都只算一次。
  • 有正有负。

完美符合要求!

只需要打个补丁,选 [i,i] 必选 ai


会最大权闭合子图的可以跳过下面一部分。

最大权闭合子图模型

考虑最优为选所有正的,不选负的。

建图:

  • s 为源点,t 为汇点。
  • 对于依赖关系 xy,连边 (x,y,INF)
  • 对于节点,若代价为正,连边 (s,i,vi);反之,连边 (i,t,vi)

若割掉与 s 相连的边,代表不选这个正的,损失 vi 贡献;若割掉与 t 相连的边,代表选这个负的,付出 vi 代价。

发现 st 只会通过形如 sxyt 的边相连。

  • 如果保留 (y,t),即选 y,则必然割掉 (s,x),即不选 x
  • 如果割掉 (y,t),即不选 y(s,x) 可割可不割,不受影响。

故此图的割满足y 是选 x 的必要条件

最大权为 所有正权值之和 最小割。


讲到这里本题建图就很简单了,不懂的请自行看代码理解。

#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;
}
posted @   Cindy_Li  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示