[六省联考2017]寿司餐厅(网络流+最大权闭合子图)
作者:互联网
这是一道比较好的网络流题。
看到这题,除了指数级别的暴力和所有a[i]相同的DP,并没有啥办法,只能有70pts。然后一眼就是网络流,考虑把区间变为点,对于(i,j),如果选了,则必须选(i+1,j)和(i,j-1),流量为inf。然后根据d[i][j]的正负性,正的就由源点连边,反之连向汇点。当且仅当i=j的区间,需要向每一个寿司连边,发现每种寿司选c次,计算c个id和1个m*id2,然后对每一种寿司连m*id2的流量,表示选了这种寿司一定会花这些钱,然后对每个寿司连流量id的边到其种类即可。
#include<bits/stdc++.h> using namespace std; const int N=13005; struct edge{int v,nxt,w;}e[N*10]; int n,m,ecnt=1,S,T,ans,cnt,hd[N],bel[120],d[120][120],id[120][120],lv[N]; void adde(int x,int y,int z) { e[++ecnt]=(edge){y,hd[x],z},hd[x]=ecnt; e[++ecnt]=(edge){x,hd[y],0},hd[y]=ecnt; } bool bfs() { queue<int>q;q.push(S); memset(lv,0,sizeof lv); lv[S]=1; while(!q.empty()) { int u=q.front();q.pop(); if(u==T)return 1; for(int i=hd[u];i;i=e[i].nxt) if(!lv[e[i].v]&&e[i].w)lv[e[i].v]=lv[u]+1,q.push(e[i].v); } return lv[T]; } int dfs(int u,int low) { if(u==T||!low)return low; int sum=0; for(int i=hd[u];i&&low;i=e[i].nxt) if(lv[e[i].v]==lv[u]+1&&e[i].w) { int tmp=dfs(e[i].v,min(low,e[i].w)); if(tmp>0)e[i].w-=tmp,e[i^1].w+=tmp,sum+=tmp,low-=tmp; } if(!sum)lv[u]=-1; return sum; } int main() { scanf("%d%d",&n,&m); T=12500; for(int i=1;i<=n;i++)scanf("%d",&bel[i]); for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) scanf("%d",&d[i][j]); for(int i=1;i<=1000;i++)adde(n+i,T,m*i*i); for(int i=1;i<=n;i++)adde(i,n+bel[i],1e9),adde(i,T,bel[i]); cnt=n+1000; for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) id[i][j]=++cnt; for(int i=1;i<=n;i++) { adde(id[i][i],i,1e9); if(d[i][i]>0)ans+=d[i][i],adde(S,id[i][i],d[i][i]); else adde(id[i][i],T,-d[i][i]); } for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) { adde(id[i][j],id[i][j-1],1e9),adde(id[i][j],id[i+1][j],1e9); if(d[i][j]>0)ans+=d[i][j],adde(S,id[i][j],d[i][j]); else adde(id[i][j],T,-d[i][j]); } while(bfs())ans-=dfs(S,1e9); printf("%d",ans); }View Code
标签:tmp,六省,int,子图,id,lv,low,联考,hd 来源: https://www.cnblogs.com/hfctf0210/p/11122779.html