bzoj 4842 [Neerc2016]Delight for a Cat 最小费用最大流,线性规划

题意:有n个小时,对于第i个小时,睡觉的愉悦值为si,打隔膜的愉悦值为ei,同时对于任意一段连续的k小时,必须至少有t1时间在睡觉,t2时间在打隔膜。如果要获得的愉悦值尽
量大,求最大的愉悦值和睡觉还是打隔膜的方案。(输出两行,第一行为最大愉悦值,第二行n个字符第i个字符为S则表示第i个小时在睡觉,为E则表示第i个小时在打隔膜)
我现在还没学线性规划的一系列东西(真丢人),写一下直观上的理解,学完线性规划和差分建图再回来写题解。
直观理解

首先当睡觉或者打隔膜任意一个活动满足其时间条件时,另一个也一定满足时间条件。

1.先考虑打隔膜的时间必须为t2时怎么解决。

设起初所有时间都在睡觉,那么我们需要这个睡觉的权值和-最小的sigma( si-ei )。

对每第i个点向第i+k个点引一条边,不存在则引向结尾的点,流量为1,消费为si-ei。(这条边表示第 i 小时选择了打隔膜)

虚点引到[ 1 , k ]点各一条边,流量无限,消费为0。 起始点引向虚点一条边,流量为t2。

这样建图保证了第i个点和第i+k个点一定一起取,从而保证了每个区间最后都一定有t2个时间在打隔膜(可以证明 : 必须有t2个时间打隔膜时,一定有i和i+k一起取)。

2.现在考虑可以随意使用的k-t1-t2个时间。

对第i个点向第i+1个点引一条边,流量为k-t1-t2,消费为0。此时起始点向虚点的边流量应变为k-t1。

显然有t2条路的选择和1中的没有什么分别。但i到i+1的路和起始点更多的流量给选择更多的打隔膜边留下了机会,我们还可以在每个k区间多选k-t1-t2条打隔膜边。

现在符合题意了

然后跑费用流。emm算是需要记忆的经典模型(其实只是我自己写不出来)?

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<queue>
 7 using namespace std;
 8 #define LL long long
 9 const int maxn=11000;
10 const int minf=1000000;
11 int n,k,t1,t2;
12 int SS,S,T;
13 LL a[maxn]={},b[maxn]={};
14 struct nod{
15     int x,y,v,next,rev;LL co;
16 }e[maxn*10];
17 int head[maxn]={},tot=0;
18 LL dis[maxn]={}; int fa[maxn]={};bool vis[maxn]={};
19 void init(int x,int y,int v,LL co){
20     e[++tot].x=x;e[tot].y=y;e[tot].v=v;e[tot].co=co;
21     e[tot].next=head[x];head[x]=tot;e[tot].rev=tot+1;
22     
23     e[++tot].x=y;e[tot].y=x;e[tot].v=0;e[tot].co=-co;
24     e[tot].next=head[y];head[y]=tot;e[tot].rev=tot-1;
25 }
26 queue<int>q;
27 bool SPFA(){
28     memset(vis,0,sizeof(vis));
29     memset(fa,0,sizeof(fa));
30     memset(dis,31,sizeof(dis));
31     //cout<<dis[1]<<endl;
32     q.push(S);vis[S]=1;dis[S]=0;
33     while(!q.empty()){
34         int x=q.front();q.pop();vis[x]=0;
35         for(int i=head[x];i;i=e[i].next){
36             if(e[i].v){
37                 if(dis[e[i].y]>dis[x]+e[i].co){
38                     dis[e[i].y]=dis[x]+e[i].co;
39                     fa[e[i].y]=i;
40                     if(!vis[e[i].y]){
41                         vis[e[i].y]=1;
42                         q.push(e[i].y);
43                     }
44                 }
45             }
46         }
47     }
48     return fa[T];
49 }
50 LL fyl(){
51     int val=minf;LL ans=0;
52     for(int i=fa[T];i;i=fa[e[i].x])val=min(val,e[i].v);
53     for(int i=fa[T];i;i=fa[e[i].x]){
54         ans+=e[i].co;e[i].v-=val;e[e[i].rev].v+=val;
55     }
56     return ans;
57 }
58 int main(){
59     scanf("%d%d%d%d",&n,&k,&t1,&t2);
60     SS=n+1;S=SS+1;T=S+1;
61     for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
62     for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
63     for(int i=1;i<=n;i++)init(i,i+k>n?T:i+k,1,a[i]-b[i]);
64     for(int i=1;i<=n;i++)init(i,i+1>n?T:i+1,k-t1-t2,0);
65     for(int i=1;i<=k;i++)init(SS,i,minf,0);
66     init(S,SS,k-t1,0);
67     LL ans=0;
68     for(int i=1;i<=n;i++)ans+=a[i];
69     while(SPFA())ans-=fyl();
70     printf("%lld\n",ans);
71     for(int i=1;i<2*n;i+=2){
72         if(!e[i].v)printf("E");
73         else printf("S");
74     }printf("\n");
75     return 0;
76 }
View Code

 

转载于:https://www.cnblogs.com/137shoebills/p/8871648.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值