码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj4873: [Shoi2017]寿司餐厅(最小割)

时间:2018-09-01 23:48:21      阅读:161      评论:0      收藏:0      [点我收藏+]

标签:memset   tar   类型   最小割   code   区间   传送门   flow   php   

传送门

 

大佬们是怎么一眼看出这是一个最大权闭合子图的……大佬好强->这里

1.把所有区间$(i,j)$看成一个点,如果权值大于0,则从$S$向他连边,容量为权值,否则从它向$T$连边,容量为权值的相反数

2.对于区间$(i,j)$,向所有的寿司$i$到$j$连边,表示选这个区间这些寿司必须选

3.对每一个寿司类型$w[i]$,为他们各自开一个点,向$T$连边,容量为$m*w[i]*w[i]$

4.对每一个寿司向他们所属的类型$w[i]$连边,容量为$inf$,向$T$连边,容量为$w[i]$

5.对于所有区间$(i,j)$,向区间$(i-1,j),(i,j-1)$连边,容量$inf$,表示选了大的必须选小的

然后用总收益减去最小割就行了

  1 //minamoto
  2 #include<iostream>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<queue>
  6 #define inf 0x3f3f3f3f
  7 #define ll long long
  8 using namespace std;
  9 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 10 char buf[1<<21],*p1=buf,*p2=buf;
 11 inline int read(){
 12     #define num ch-‘0‘
 13     char ch;bool flag=0;int res;
 14     while(!isdigit(ch=getc()))
 15     (ch==-)&&(flag=true);
 16     for(res=num;isdigit(ch=getc());res=res*10+num);
 17     (flag)&&(res=-res);
 18     #undef num
 19     return res;
 20 }
 21 const int N=50005,M=2000005;
 22 int head[N],Next[M],ver[M],edge[M],tot=1;
 23 int dep[N],cur[N],S,T;
 24 queue<int> q;
 25 inline void add(int u,int v,int e){
 26     ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;
 27     ver[++tot]=u,Next[tot]=head[v],head[v]=tot,edge[tot]=0;
 28 }
 29 bool bfs(){
 30     while(!q.empty()) q.pop();
 31     for(int i=S;i<=T;++i) cur[i]=head[i];
 32     memset(dep,-1,sizeof(dep));
 33     q.push(S),dep[S]=0;
 34     while(!q.empty()){
 35         int u=q.front();q.pop();
 36         for(int i=head[u];i;i=Next[i]){
 37             int v=ver[i];
 38             if(dep[v]<0&&edge[i]){
 39                 dep[v]=dep[u]+1,q.push(v);
 40                 if(v==T) return true;
 41             }
 42         }
 43     }
 44     return false;
 45 }
 46 int dfs(int u,int limit){
 47     if(u==T||!limit) return limit;
 48     int flow=0,f;
 49     for(int i=cur[u];i;i=cur[u]=Next[i]){
 50         int v=ver[i];
 51         if(dep[v]==dep[u]+1&&(f=dfs(v,min(limit,edge[i])))){
 52             flow+=f,limit-=f;
 53             edge[i]-=f,edge[i^1]+=f;
 54             if(!limit) break;
 55         }
 56     }
 57     if(!flow) dep[u]=-1;
 58     return flow;
 59 }
 60 int dinic(){
 61     int flow=0;
 62     while(bfs()) flow+=dfs(S,inf);
 63     return flow;
 64 }
 65 int n,m,a[105],mp[105][105],id[105][105],cnt=0;
 66 int idw[1005];bool vis[1005];ll sum=0;
 67 void build(){
 68     S=0;
 69     for(int i=1;i<=n;++i)
 70     for(int j=1;j<=n;++j)
 71     id[i][j]=++cnt;
 72     for(int i=1;i<=n;++i)
 73     if(!vis[a[i]]) vis[a[i]]=1,idw[a[i]]=++cnt;
 74     T=cnt+n+1;
 75     memset(vis,0,sizeof(vis));
 76     for(int i=1;i<=n;++i)
 77     if(!vis[a[i]]) vis[a[i]]=1,add(idw[a[i]],T,m*a[i]*a[i]);
 78     for(int i=1;i<=n;++i)
 79     add(i+cnt,idw[a[i]],inf),add(i+cnt,T,a[i]);
 80     for(int i=1;i<=n;++i)
 81     for(int j=i;j<=n;++j){
 82         if(mp[i][j]>0){
 83             sum+=mp[i][j];
 84             add(S,id[i][j],mp[i][j]);
 85             add(id[i][j],i+cnt,inf);
 86             add(id[i][j],j+cnt,inf);
 87         }
 88         else if(mp[i][j]<0){
 89             add(id[i][j],T,-mp[i][j]);
 90             add(id[i][j],i+cnt,inf);
 91             add(id[i][j],j+cnt,inf);
 92         }
 93         if(i!=j){
 94             add(id[i][j],id[i+1][j],inf);
 95             add(id[i][j],id[i][j-1],inf);
 96         }
 97     }
 98 }
 99 int main(){
100     //freopen("testdata.in","r",stdin);
101     n=read(),m=read();
102     for(int i=1;i<=n;++i) a[i]=read();
103     for(int i=1;i<=n;++i)
104     for(int j=i;j<=n;++j)
105     mp[i][j]=read();
106     build();
107     printf("%lld\n",sum-dinic());
108     return 0;
109 }

 

bzoj4873: [Shoi2017]寿司餐厅(最小割)

标签:memset   tar   类型   最小割   code   区间   传送门   flow   php   

原文地址:https://www.cnblogs.com/bztMinamoto/p/9571630.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!