P3749 [六省聯考 2017] 壽司餐廳 分析
最大權閉合子圖 的模型
模型:有若干個物品,每種物品有一個價值 $v_i$,選取了物品就意味著獲得了價值。
物品之間有限制關係:$x \to y$ 表示若要選擇物品 $x$ 則必須選擇物品 $y$。目標是最大化價值和。
解法
考慮使用最小割解決此類問題:
將每個物品與源點 $s$ 和匯點 $t$ 相連。若割掉與 $s$ 相連的邊表示不選這個物品,割掉與 $t$ 相連的邊表示選擇這個物品。
對於一個物品的價值 $v$,如果 $v>0$ 則令它與 $s$ 相連的邊的權值為 $v$,與 $t$ 相連的邊的權值為 $0$,將 $v$ 加入答案。表示不選擇這個物品會付出 $v$ 的代價;如果 $v<0$ 則令它與 $s$ 相連的邊的權值為 $0$,與 $t$ 相連的邊的權值為 $-v$,表示選擇這個物品會付出 $-v$ 的代價。
對於 $x \to y$ 的關係,轉化為 $x$ 向 $y$ 連一條權值極大的邊,顯然這條邊永遠不會被割,如果選擇了 $x$,即割掉 $x$ 與 $t$ 相連的邊,那麼如果不選 $y$,即割掉 $y$ 與 $s$ 相連的邊,就會出現路徑 $s \to x \to y \to t$,所以必須選擇 $y$。而如果不選 $x$ 則對 $y$ 的選擇沒有影響。
至此,問題符合使用 Dinic 演算法解決網路流的條件,結合最大流-最小割定理,可以使用 Dinic 演算法解決此類問題。
轉化
在本題中,吃了每種型別為 $x$ 的壽司需要付出 $x$ 的代價,而吃過型別為 $x$ 的壽司需要付出 $mx^2$ 的代價。考慮將每種 $d_{i,j}$ 的收益看成物品,顯然如果選擇 $d_{i,j}$($i<j$),則必須選擇 $d_{i,j-1}$ 以及 $d_{i+1,j}$。
至此,轉化完成,可以用最大權閉合子圖的模型求解。
namespace LZX { using namespace std; typedef long long i64; const int MAXN=6060,MAXM=16060; const i64 INF=0x7fffffffffffffff; int s,t,cnt=1,head[MAXN],to[MAXM<<1],nxt[MAXM<<1],pre[MAXN],dep[MAXN],d[105][105],id[105][105],a[105]; i64 f[MAXM<<1]; void g_flow_add(int a,int b,i64 c) { nxt[++cnt]=head[a]; head[a]=cnt; to[cnt]=b; f[cnt]=c; nxt[++cnt]=head[b]; head[b]=cnt; to[cnt]=a; f[cnt]=0; } bool g_flow_BFS() { int u,v; queue<int> q; memset(dep,0,sizeof(dep)); dep[s]=1; q.push(s); do { u=q.front(); q.pop(); for(int i=head[u];i;i=nxt[i]) { v=to[i]; if(f[i]&&!dep[v]) { dep[v]=dep[u]+1; q.push(v); } } } while(!q.empty()); return dep[t]; } i64 g_flow_DFS(int u,i64 flow) { i64 res,v,temp; if(u==t) { return flow; } res=0; for(int &i=pre[u];i;i=nxt[i]) { v=to[i]; if(f[i]&&dep[v]==dep[u]+1) { temp=g_flow_DFS(v,min(flow,f[i])); f[i]-=temp; f[i^1]+=temp; flow-=temp; res+=temp; if(!flow) { break; } } } return res; } i64 g_flow_dinic() { i64 res=0; while(g_flow_BFS()) { memmove(pre,head,sizeof(head)); res+=g_flow_DFS(s,INF); } return res; } int _main() { int n,m,k,maxa,cost; i64 ans=0; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",a+i); maxa=max(maxa,a[i]); } s=1; t=2; k=2; for(int i=1;i<=n;i++) { for(int j=i;j<=n;j++) { scanf("%d",&d[i][j]); id[i][j]=++k; } } for(int i=1;i<=n;i++) { for(int j=i;j<=n;j++) { cost=d[i][j]; if(i==j) { if(m) { g_flow_add(id[i][j],a[i]+k,INF); } cost-=a[i]; } else { g_flow_add(id[i][j],id[i+1][j],INF); g_flow_add(id[i][j],id[i][j-1],INF); } if(cost>0) { g_flow_add(s,id[i][j],cost); ans+=1ll*cost; } else if(cost<0) { g_flow_add(id[i][j],t,-cost); } } } if(m) { for(int i=1;i<=maxa;i++) { g_flow_add(++k,2,i*i); } } printf("%lld\n",ans-g_flow_dinic()); return 0; } }