1095 字
5 分钟
BZOJ 4873 [Shoi2017]寿司餐厅 网络流
2017-06-14

Description#

Kiana最近喜欢到一家非常美味的寿司餐厅用餐。每天晚上,这家餐厅都会按顺序提供n种寿司,第i种寿司有一个 代号ai和美味度di,i,不同种类的寿司有可能使用相同的代号。每种寿司的份数都是无限的,Kiana也可以无限次 取寿司来吃,但每种寿司每次只能取一份,且每次取走的寿司必须是按餐厅提供寿司的顺序连续的一段,即Kiana 可以一次取走第1,2种寿司各一份,也可以一次取走第2,3种寿司各一份,但不可以一次取走第1,3种寿司。由于餐 厅提供的寿司种类繁多,而不同种类的寿司之间相互会有影响:三文鱼寿司和鱿鱼寿司一起吃或许会很棒,但和水

果寿司一起吃就可能会肚子痛。因此,Kiana定义了一个综合美味度d(i,j)(i<j),表示在一次取的寿司中,如果包含 了餐厅提供的从第i份到第j份的所有寿司,吃掉这次取的所有寿司后将获得的额外美味度。由于取寿司需要花费一 些时间,所以我们认为分两次取来的寿司之间相互不会影响。注意在吃一次取的寿司时,不止一个综合美味度会被 累加,比如若Kiana一次取走了第1,2,3种寿司各一份,除了d(1,3)以外,d(1,2),d(2,3)也会被累加进总美味度中。神奇 的是,Kiana的美食评判标准是有记忆性的,无论是单种寿司的美味度,还是多种寿司组合起来的综合美味度,在 计入Kiana的总美味度时都只会被累加一次。比如,若Kiana某一次取走了第1,2种寿司各一份,另一次取走了第2,3 种寿司各一份,那么这两次取寿司的总美味度为d(1,1)+d(2,2)+d(3,3)+d(1,2)+d(2,3),其中d(2,2)只会计算一次。奇怪的是, 这家寿司餐厅的收费标准很不同寻常。具体来说,如果Kiana一共吃过了c(c>0)种代号为x的寿司,则她需要为这些 寿司付出mx^2+cx元钱,其中m是餐厅给出的一个常数。现在Kiana想知道,在这家餐厅吃寿司,自己能获得的总美 味度(包括所有吃掉的单种寿司的美味度和所有被累加的综合美味度)减去花费的总钱数的最大值是多少。由于她 不会算,所以希望由你告诉她

Input#

第一行包含两个正整数n,m,分别表示这家餐厅提供的寿司总数和计算寿司价格中使用的常数。 第二行包含n个正整数,其中第k个数ak表示第k份寿司的代号。 接下来n行,第i行包含n-i+1个整数,其中第j个数d(i,i+j-1)表示吃掉寿司能 获得的相应的美味度,具体含义见问题描述。 N<=100,Ai<=1000

Output#

输出共一行包含一个正整数,表示Kiana能获得的总美味度减去花费的总钱数的最大值。

Sample Input#

3 1
2 3 2
5 -10 15
-10 15
15

Sample Output#

12

题解#

将区间看做点 每个[i,j]区间只要向[i+1,j],[i,j-1]连边
转换为最大权闭合子图
现在最麻烦的是解决每个点的权值
将单个的点即[i,j] (i==j)每个点的美味度减去它的代号(解决cm)向 Idx[a[i]]连边 Idx[]用来限制第一次吃的额外的花费

剩下的就和标准的最大权闭合子图一样了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
const int INF=0x7fffffff;
int idIndex;
int read(){
    int res=0;
    static char ch;int flag=1;
	while((ch=getchar())<'0'||ch>'9')if(ch=='-')flag=-1;res=ch-48;
	while((ch=getchar())>='0'&&ch<='9')res=(res<<1)+(res<<3)+ch-48;
    return res*flag;
}
struct edge{
    int END,next,cap;
}v[1000005];
int first[10005],p;
void add(int a,int b,int c){
    v[p].END=b;v[p].next=first[a];v[p].cap=c;first[a]=p++;
    v[p].END=a;v[p].next=first[b];v[p].cap=0;first[b]=p++;
}
int a[150];
int Idx[1005],id[150][150];
int dis[10005],cur[10005];
bool vis[10005];
bool BFS(int S,int E){
    memset(dis,-1,sizeof(dis));
    memset(vis,0,sizeof(vis));
    vis[S]=1;dis[S]=0;
    queue<int> Q;
    Q.push(S);
    while(!Q.empty()){
        int u=Q.front();Q.pop();
        for(int i=first[u];i!=-1;i=v[i].next){
            if(!vis[v[i].END]&&v[i].cap>0){
                vis[v[i].END]=1;
                dis[v[i].END]=dis[u]+1;
                if(v[i].END==E)return 1;
                Q.push(v[i].END);
            }
        }
    }
    return 0;
}
int DFS(int S,int E,int a){
    if(S==E||a==0)return a;
    int flow=0,f;
    for(int &i=cur[S];i!=-1;i=v[i].next){
        if(dis[v[i].END]==dis[S]+1&&(f=DFS(v[i].END,E,min(a,v[i].cap)))>0){
            v[i].cap-=f;
            v[i^1].cap+=f;
            a-=f;
            flow+=f;
            if(f==0)break;
        }
    }
    //
    if(!flow) dis[S]=-1;
    return flow; 
}
int Dinic(int S,int E){
    int ans=0;
    while(BFS(S,E)){
        memcpy(cur,first,sizeof(first));
        ans+=DFS(S,E,INF);
    }
    return ans;
}
int main()
{
    int n,m;
    memset(first,-1,sizeof(first));
    scanf("%d%d",&n,&m);
    int S=++idIndex,T=++idIndex;
    int maxn=0;
    for(int i=1;i<=n;i++) maxn=max(maxn,a[i]=read());
    for(int i=1;i<=maxn;i++){
        Idx[i]=++idIndex;
        add(Idx[i],T,m*i*i);
    }
    for(int i=1;i<=n;i++)for(int j=i;j<=n;j++)id[i][j]=++idIndex;
    int x;
    int ans=0;
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j++){
            x=read();
            if(i==j)x-=a[i],add(id[i][j],Idx[a[i]],INF);
            else {
                if(id[i+1][j])add(id[i][j],id[i+1][j],INF);
                if(id[i][j-1])add(id[i][j],id[i][j-1],INF);
            }
            if(x>0) ans+=x,add(S,id[i][j],x);
            else add(id[i][j],T,-x);
        }
    }
    ans-=Dinic(S,T);
    printf("%d\n",ans);
    getchar();
    getchar();
    return 0;
}
BZOJ 4873 [Shoi2017]寿司餐厅 网络流
https://www.nekomio.com/posts/11/
作者
NekoMio
发布于
2017-06-14
许可协议
CC BY-NC-SA 4.0