zoukankan      html  css  js  c++  java
  • Delight for a Cat

    Time Limit: 1000 ms Memory Limit: 512 MB

    Description

    ​ 从前,有一只懒猫叫CJB。每个小时,这只猫要么在睡觉,要么在吃东西,但不能一边睡觉一边吃东西,并且这只猫会在一整个小时干同一件事情。

    ​ 对于接下来的n个小时,CJB知道他在那n个小时睡觉和吃东西的快乐值。

    ​ 为了健♂康♀地生活,在任意的连续 k个整小时内,CJB要有至少(m_s)小时睡觉,至少(m_e)个小时在吃东西。也就是说一共有 n−k+1段的 k小时需要满足上述条件。

    你的任务是告诉CJB他接下来n个小时能获得的最大快乐值是多少。

    Input

    ​ 第一行四个整数 (n,k,ms,me(1≤k≤n≤1000,0≤m_s,m_e≤k,m_s+m_e≤k)),含义如题目所述。

    ​ 第二行包含 n个整数 (s_1,s_2,...,s_n(0≤s_i≤10^9)),表示每个小时CJB睡觉能获得的快乐值。

    ​ 第三行包含 n个整数 (e_1,e_2,...,e_n(0≤e_i≤10^9)),表示每个小时CJB吃东西能获得的快乐值。

    Output

    输出一个整数,表示CJB能获得的最大快乐值。

    Sample Input

    10 4 1 2
    1 2 3 4 5 6 7 8 9 10
    10 9 8 7 6 5 4 3 2 1
    

    Sample Output

    69
    

    HINT

    ​ 【样例解释】

    ​ CJB在接下来的10个小时是这样的(S表示睡觉,E表示吃东西):EEESESEESS

    ​ 【数据范围与约定】

    ​ 30%的数据中, 1≤n≤10

    ​ 100%的数据中, 1≤n≤1000


    吐槽

    这题好神啊哈哈哈qwq(好吧虽然好像印象中之前做过类似的费用流的题目。。但是当时并没有搞太懂所以看到这题的时候根本没有往那边去想qwq菜醒)

    然而为什么网上说是。。单纯形裸题??好吧听起来很棒但是并不会单纯形qwq

    所以还是写费用流吧嗯qwq

    正题

    这里要用到一种特殊的建模方式!十分巧妙啊ovo

    具体介绍戳这里(没错就是比较长不拎出来对不起自己嗯)

    首先假设这只懒猫一直在睡觉,然后我们再决策哪些天数需要改成吃东西

    (x_i =0)表示这只猫第(i)天在睡觉,(x_i=1)表示在吃东西,那么我们可以列出这样的不等式组

    [egin{aligned} &m_e<=x_1+x_2+x_3+...+x_k<=k-m_s\ &m_e<=x_2+x_3+x_4+...+x_{k+1}<=k-ms\ &...\ &m_e<=x_{n-k+1}+x_{n-k+2}+x_{n-k+3}+...+x_n<=k-m_s\ end{aligned} ]

    用同样的方法将这些不等式组变成等式,其中辅助变量(0<=y_i<=k-m_s-m_e)

    [egin{aligned} &x_1+x_2+x_3+...+x_k+y_1=k-m_s\ &x_2+x_3+x_4+...+x_{k+1}+y_2=k-m_s\ &...\ &x_{n-k+1}+x_{n-k+2}+...+x_n=k-m_s\ end{aligned} ]

    同样的套路,在一头一尾加上两个0=0等式,用上面的减去下面的得到:

    [egin{aligned} &x_1+x_2+x_3+...+x_k+y_1=k-m_s\ &x_1-x_{k+1}+y_1-y_2=0\ &x_2-x_{k+2}+y_2-y_3=0\ &...\ &x_{n-k}-x_{n}+y_{n-k}-y_{n-k+1}=0\ &x_{n_k+1}+x_{n-k+2}+...+x_{n}+y_{n-k+1}=k-m_s\ end{aligned} ]

    (啊?想用下面的减去下面的?额一样的反正都可以)

    然后按照套路:

    [egin{aligned} &x_1-x_{k+1}+y_1-y_2=0\ &x_2-x_{k+2}+y_2-y_3=0\ &...\ &x_{n-k}-x_{n}+y_{n-k}-y_{n-k+1}=0\ &(k-m_s)-x_1-x_2-x_3-...-x_k-y_1=0\ &x_{n_k+1}+x_{n-k+2}+...+x_{n}+y_{n-k+1}-(k-m_s)=0\ end{aligned} ]

    (其实倒数第二条式子是第1条式子和第0条式子相减得到,的但是为了和后面的代码中的编号对应我们稍微改变一下位置)
    观察一下,变成了这个样子之后所有式子加起来变量和常数项就都抵消掉了,满足流量平衡

    那么就用和bzoj1061同样的方式把每个等式看成一个点,负的流出正的流入就好啦

    跑完一遍最小费用最大流之后,最终的(ans)就是$sumlimits_{i=1}^{n}s_i - $最小费用

    然而这并没有结束

    首先是,这题的(x_i)(y_i)是有范围的!

    (y_i)的范围上面写了就是(0<=y_i<=k-m_s-m_e)

    (x_i)的话要么是(0)要么是(1)

    所以两个变量对应的边的容量应该分别为(k-m_s-m_e)(1)

    再有就是,如果稍微想一下等式之间的连边情况,会发现。。(k<n-k)(k>n-k)这两种情况是不一样的!

    所以说,这题还要分类讨论

    然而具体的连边方式。。写起来十分混乱不如自己去推qwq,详情可以参考代码

    (注意,代码中将上面的第一条等式放在了倒数第二条,所以编号为(n-k+1),最后一条等式的编号为(n-k+2),其他的等式依次为(1)(n-k)

    这里只讲一下大概要连哪几类边,下面所说的编号顺序按照最终给出的等式组来(也就是代码中的顺序):

    1. 第一种(y)边。从(i+1)连到(i)
    2. 第二种(y)边。从最后一条等式连到第(1)条等式,从(n-k)条等式连到倒数第二条等式
    3. 源点到(n-k+1)的边,汇点到(n-k+2)的边
    4. (1)(n-k)条等式中每个正的(x)的边。
    5. (1)(n-k)条等式中每个负的(x)的边。

    稍微提一下,前3类是固定的,第4和第5类又可以分第(1)(n-k)条连到最后两条,和(1)(n-k)条里面自己乱连两小类,具体就(k)(n)的大小关系而定

    自己写的时候把(k=n-k)的情况也分出来了,可能是。。可以合并的不过不管了反正这么写也不影响正确性

    然后就很愉快(个鬼啊分类讨论这种东西)滴做完啦ovo


    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define ll long long
    #define F 1
    using namespace std;
    const int N=10010;
    const int M=1000010;
    const ll inf=1e15;
    struct xxx{
    	int y,next,op;
    	ll c,r;
    }a[M*10];
    queue<int> q;
    int h[N];
    ll e[N],s[N];
    ll pre[M],mn[N],pred[M];
    ll d[N];
    ll ans,sum;
    bool vis[N];
    int n,m,vs,vt,tot;
    ll k,ms,me;
    int add(int x,int y,int r,ll c);
    int spfa();
    int build();
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("a.in","r",stdin);
    //	freopen("a.out","w",stdout);
    #endif
    	scanf("%d%lld%lld%lld",&n,&k,&ms,&me);
    	vs=0; vt=n-k+3;
    	sum=0;
    	for (int i=1;i<=n;++i) scanf("%lld",s+i),sum+=s[i];
    	for (int i=1;i<=n;++i) scanf("%lld",e+i);
    	build();
    	ans=0;
    	while (spfa());
    	printf("%lld
    ",sum+ans);
    }
    
    int add(int x,int y,int r,ll c){
    	a[++tot].y=y; a[tot].next=h[x]; h[x]=tot; a[tot].r=r; a[tot].op=tot+1; a[tot].c=c;
    	a[++tot].y=x; a[tot].next=h[y]; h[y]=tot; a[tot].r=0; a[tot].op=tot-1; a[tot].c=-c;
    }
    
    int spfa(){
    	while (!q.empty()) q.pop();
    	for (int i=vs;i<=vt;++i) d[i]=-inf,vis[i]=false,mn[i]=inf;
    	q.push(vs); vis[vs]=1; d[vs]=0;
    	mn[vs]=inf; pre[vs]=-1; pred[vs]=-1;
    	int v,u;
    	while (!q.empty()){
    		v=q.front(); q.pop();
    		for (int i=h[v];i!=-1;i=a[i].next){
    			u=a[i].y;
    			if (!a[i].r) continue;
    			if (d[u]<d[v]+a[i].c){
    				pre[u]=i; pred[u]=v;
    				mn[u]=min(mn[v],a[i].r);
    				d[u]=d[v]+a[i].c;
    				if (!vis[u])
    					vis[u]=1,q.push(u);
    			}
    		}
    		vis[v]=false;
    	}
    	if (d[vt]==-inf) return false;
    	ll flow=mn[vt];
    	ans+=flow*d[vt];
    	u=vt;
    	while (pre[u]!=-1){
    		a[pre[u]].r-=flow;
    		a[a[pre[u]].op].r+=flow;
    		u=pred[u];
    	}
    	return true;
    }
    
    int build(){
    	memset(h,-1,sizeof(h));
    	if (k<n-k){
    		/*X*/
    		for (int i=1;i<=k;++i) add(n-k+1,i,F,e[i]-s[i]);
    		for (int i=n-2*k+1;i<=n-k;++i) add(i,n-k+2,F,e[i+k]-s[i+k]);
    		for (int i=1;i<=n-2*k;++i) add(i,i+k,F,e[i+k]-s[i+k]);
    	}
    	else if (k==n-k){
    		/*X*/
    		for (int i=1;i<=n-k;++i) add(n-k+1,i,F,e[i]-s[i]);
    		for (int i=1;i<=n-k;++i) add(i,n-k+2,F,e[i+k]-s[i+k]);
    	}
    	else{
    		/*X*/
    		for (int i=1;i<=n-k;++i) add(n-k+1,i,F,e[i]-s[i]);
    		for (int i=1;i<=n-k;++i) add(i,n-k+2,F,e[i+k]-s[i+k]);
    		for (int i=n-k+1;i<=k;++i) add(n-k+1,n-k+2,F,e[i]-s[i]);
    	}
    	/*Y*/for (int i=1;i<=n-k-1;++i) add(i,i+1,k-ms-me,0);
    	/*Y*/add(n-k+1,1,k-ms-me,0); add(n-k,n-k+2,k-ms-me,0);
    	/*X*/add(vs,n-k+1,k-ms,0); add(n-k+2,vt,k-ms,0);
    }
    
  • 相关阅读:
    第四次作业
    随机点名
    表单验证
    冒泡排序&&选择排序
    Equals相等
    String类
    最终类final
    Eclipse的设置
    Equals相等(测试)
    猜数字
  • 原文地址:https://www.cnblogs.com/yoyoball/p/8268901.html
Copyright © 2011-2022 走看看