【BZOJ】【3550】【ONTAK2010】Vacation

网络流/费用流


  Orz太神犇了这题……

  我一开始想成跟Intervals那题一样了……每个数a[i]相当于覆盖了(a[i]-n,a[i]+n)这个区间……但是这样是错的!!随便就找出反例了……我居然还一直当正解……

  实际上刚刚那个思路还有一个问题:题目中的长度为N的区间指的是给的原序列!而不是权值的区间!题就理解错了……

  看了下zyf的题解,才明白过来,要用跟志愿者招募一样的方法来做;另外,志愿者招募时每种志愿者是有无限多名的,但这题中每个数只能选一次,所以边权为a[i]的边的流量不能是INF,而是1。

题解:
做这题真是一种折磨。。。
复习了一下根据流量平衡方程建图的方法,主要是膜拜了byvoid的志愿者招募的题解。。。
让我们先列出流量平衡方程:a[i]表示i选不选,b[i]表示第i个等式的辅助变量
则:
0=0
a[1]+a[2]+……a[n]+b[1]=k
a[2]+a[3]+……a[n+1]+b[2]=k
…………
a[2*n+1]+a[2*n+2]+……+a[3*n]+b[2*n+1]=k
0=0
差分之后是
a[1]+a[2]+……a[n]+b[1]=k
a[n+1]-a[1]+b[2]-b[1]=0
a[n+2]-a[2]+b[3]-b[2]=0
…………
-a[2*n+1]-a[2*n+2]-………-a[3*n]-b[2*n+1]=-k
 
根据BYVOID所说的这段话:
可以发现,每个等式左边都是几个变量和一个常数相加减,右边都为0,恰好就像网络流中除了源点和汇点的顶点都满足流量平衡。每个正的变量相当于流入该顶点的流量,负的变量相当于流出该顶点的流量,而正常数可以看作来自附加源点的流量,负的常数是流向附加汇点的流量。因此可以据此构造网络,求出从附加源到附加汇的网络最大流,即可满足所有等式。而我们还要求noi_employee_3最小,所以要在X变量相对应的边上加上权值,然后求最小费用最大流
 
所以我们构图:
复制代码
    s=0;t=3*n+1;
    for1(i,3*n)a[i]=read();
    insert(s,1,k,0);insert(2*n+2,t,k,0);
    for1(i,n)insert(1,i+1,1,-a[i]);
    for2(i,n+1,2*n)insert(i-n+1,i+1,1,-a[i]);
    for2(i,2*n+1,3*n)insert(i-n+1,2*n+2,1,-a[i]);
    for1(i,2*n+1)insert(i,i+1,inf,0);
复制代码

需要搞清楚a[i]在哪个等式中第一次出现,在哪个等式中第二次出现,以及正负号各是什么。

b[i]的出现很显然,i为正,i+1为负

然后求最大费用最大流就可以过了。

  1 /**************************************************************
  2     Problem: 3550
  3     User: Tunix
  4     Language: C++
  5     Result: Accepted
  6     Time:20 ms
  7     Memory:6016 kb
  8 ****************************************************************/
  9 
 10 //BZOJ 3550
 11 #include<cmath>
 12 #include<vector>
 13 #include<cstdio>
 14 #include<cstring>
 15 #include<cstdlib>
 16 #include<iostream>
 17 #include<algorithm>
 18 #define rep(i,n) for(int i=0;i<n;++i)
 19 #define F(i,j,n) for(int i=j;i<=n;++i)
 20 #define D(i,j,n) for(int i=j;i>=n;--i)
 21 #define pb push_back
 22 #define CC(a,b) memset(a,b,sizeof(a))
 23 using namespace std;
 24 int getint(){
 25     int v=0,sign=1; char ch=getchar();
 26     while(!isdigit(ch)) {if(ch=='-') sign=-1; ch=getchar();}
 27     while(isdigit(ch))  {v=v*10+ch-'0'; ch=getchar();}
 28     return v*sign;
 29 }
 30 typedef long long LL;
 31 const int N=2000,M=200000,INF=~0u>>2;
 32 const double eps=1e-8;
 33 /*******************template********************/
 34 int n,m,k,a[N],b[N],c[N],w[N];
 35 LL ans;
 36 struct edge{int from,to,v,c;};
 37 struct Net{
 38     edge E[M];
 39     int head[N],next[M],cnt;
 40     void ins(int x,int y,int z,int c){
 41         E[++cnt]=(edge){x,y,z,c};
 42         next[cnt]=head[x]; head[x]=cnt;
 43     }
 44     void add(int x,int y,int z,int c){
 45         ins(x,y,z,c); ins(y,x,0,-c);
 46     }
 47     int S,T,d[N],from[N],Q[M];
 48     bool inq[N];
 49     bool spfa(){
 50         int l=0,r=-1;
 51         F(i,0,T)d[i]=INF;
 52         d[S]=0; Q[++r]=S; inq[S]=1;
 53         while(l<=r){
 54             int x=Q[l++]; inq[x]=0;
 55             for(int i=head[x];i;i=next[i])
 56                 if(E[i].v && d[x]+E[i].c<d[E[i].to]){
 57                     d[E[i].to]=d[x]+E[i].c;
 58                     from[E[i].to]=i;
 59                     if(!inq[E[i].to]){
 60                         Q[++r]=E[i].to;
 61                         inq[E[i].to]=1;
 62                     }
 63                 }
 64         }
 65         return d[T]!=INF;
 66     }
 67     void mcf(){
 68         int x=INF;
 69         for(int i=from[T];i;i=from[E[i].from])
 70             x=min(x,E[i].v);
 71         for(int i=from[T];i;i=from[E[i].from]){
 72             E[i].v-=x;
 73             E[i^1].v+=x;
 74         }
 75         ans+=x*d[T];
 76     }
 77     void init(){
 78         m=n=getint(); m*=3; k=getint(); cnt=1;
 79         F(i,1,m) a[i]=getint();
 80         S=0; T=2*n+3;
 81         add(S,1,k,0);
 82         add(n*2+2,T,k,0);
 83         F(i,1,n*2+1) add(i,i+1,INF,0);
 84         
 85         F(i,2,n+1) add(1,i,1,-a[i-1]);
 86         F(i,n+2,2*n+1) add(i,2*n+2,1,-a[i+n-1]);
 87         F(i,2,n+1) add(i,i+n,1,-a[i+n-1]);
 88         
 89         while(spfa()) mcf();
 90         printf("%lld
",-ans);
 91     }
 92 }G1;
 93 int main(){
 94 #ifndef ONLINE_JUDGE
 95     freopen("input.txt","r",stdin);
 96 //    freopen("output.txt","w",stdout);
 97 #endif
 98     G1.init();
 99     return 0;
100 }
View Code

3550: [ONTAK2010]Vacation

Time Limit: 10 Sec  Memory Limit: 96 MB
Submit: 141  Solved: 105
[Submit][Status][Discuss]

Description

有3N个数,你需要选出一些数,首先保证任意长度为N的区间中选出的数的个数<=K个,其次要保证选出的数的个数最大。

Input

第一行两个整数N,K。
第二行有3N个整数。

Output

一行一个整数表示答案。

Sample Input

5 3
14 21 9 30 11 8 1 20 29 23 17 27 7 8 35

Sample Output

195

HINT

【数据范围】

N<=200,K<=10。

Source

[Submit][Status][Discuss]
原文地址:https://www.cnblogs.com/Tunix/p/4353035.html