【BZOJ1283/3550】序列/[ONTAK2010]Vacation 最大费用流

【BZOJ1283】序列

Description

给出一个长度为 的正整数序列Ci,求一个子序列,使得原序列中任意长度为 的子串中被选出的元素不超过K(K,M<=100) 个,并且选出的元素之和最大。

Input

第1行三个数N,m,k。 接下来N行,每行一个字符串表示Ci。

Output

最大和。

Sample Input

10 5 3
4 4 4 6 6 6 6 6 4 4

Sample Output

30

HINT

20%的数据:n<=10。
100%的数据:N<=1000,k,m<=100。Ci<=20000。

题解:很难想的费用流建图,看了题解才略懂,下面说一下建图方法和我的理解:

1.S->1...i -> i+1...n->T 容量k,费用0
2.i -> i+m 容量1,费用ai

我的理解是:假如你只有k个流量,要体现出所有的权值,你该如何利用这k个流量?显然你必须重复利用这些流量,就以[l,l+m]和[l+1,l+m+1],l的流量对l+m+1没有影响,所以l+m+1可以直接将l的流量拿过来用,达到节约流量的目的。这样一来,这k个流量在经过每个区间时都会选择权值最大的路径去走,这样跑最大费用流就能得出正确的解。

1283

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
int n,m,k,S,T,cnt,ans;
int to[30000],next[30000],head[1010],cost[30000],flow[30000],dis[1010],inq[1010],pe[1010],pv[1010];
queue<int> q;
void add(int a,int b,int c,int d)
{
	to[cnt]=b,cost[cnt]=c,flow[cnt]=d,next[cnt]=head[a],head[a]=cnt++;
	to[cnt]=a,cost[cnt]=-c,flow[cnt]=0,next[cnt]=head[b],head[b]=cnt++;
}
int bfs()
{
	memset(dis,0x3f,sizeof(dis));
	dis[S]=0,q.push(S);
	int i,u;
	while(!q.empty())
	{
		u=q.front(),q.pop(),inq[u]=0;
		for(i=head[u];i!=-1;i=next[i])
		{
			if(dis[to[i]]>dis[u]+cost[i]&&flow[i])
			{
				dis[to[i]]=dis[u]+cost[i],pe[to[i]]=i,pv[to[i]]=u;
				if(!inq[to[i]])	inq[to[i]]=1,q.push(to[i]);
			}
		}
	}
	return dis[T]<0x3f3f3f3f;
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	int i,j,a;
	S=0,T=n+1;
	memset(head,-1,sizeof(head));
	add(S,1,0,k);
	for(i=1;i<=n;i++)
	{
		add(i,i+1,0,k);
		scanf("%d",&a);
		if(i+m<=n)	add(i,i+m,-a,1);
		else	add(i,T,-a,1);
	}
	while(bfs())
	{
		int mf=1<<30;
		for(i=T;i!=S;i=pv[i])	mf=min(mf,flow[pe[i]]);
		ans-=dis[T]*mf;
		for(i=T;i!=S;i=pv[i])	flow[pe[i]]-=mf,flow[pe[i]^1]+=mf;
	}
	printf("%d",ans);
	return 0;
}

3550

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
int n,k,S,T,cnt,ans;
int to[30000],next[30000],head[1010],cost[30000],flow[30000],dis[1010],inq[1010],pe[1010],pv[1010];
queue<int> q;
void add(int a,int b,int c,int d)
{
	to[cnt]=b,cost[cnt]=c,flow[cnt]=d,next[cnt]=head[a],head[a]=cnt++;
	to[cnt]=a,cost[cnt]=-c,flow[cnt]=0,next[cnt]=head[b],head[b]=cnt++;
}
int bfs()
{
	memset(dis,0x3f,sizeof(dis));
	dis[S]=0,q.push(S);
	int i,u;
	while(!q.empty())
	{
		u=q.front(),q.pop(),inq[u]=0;
		for(i=head[u];i!=-1;i=next[i])
		{
			if(dis[to[i]]>dis[u]+cost[i]&&flow[i])
			{
				dis[to[i]]=dis[u]+cost[i],pe[to[i]]=i,pv[to[i]]=u;
				if(!inq[to[i]])	inq[to[i]]=1,q.push(to[i]);
			}
		}
	}
	return dis[T]<0x3f3f3f3f;
}
int main()
{
	scanf("%d%d",&n,&k);
	int i,j,a;
	S=0,T=3*n+1;
	memset(head,-1,sizeof(head));
	add(S,1,0,k);
	for(i=1;i<=3*n;i++)
	{
		add(i,i+1,0,k);
		scanf("%d",&a);
		if(i+n<=3*n)	add(i,i+n,-a,1);
		else	add(i,T,-a,1);
	}
	while(bfs())
	{
		int mf=1<<30;
		for(i=T;i!=S;i=pv[i])	mf=min(mf,flow[pe[i]]);
		ans-=dis[T]*mf;
		for(i=T;i!=S;i=pv[i])	flow[pe[i]]-=mf,flow[pe[i]^1]+=mf;
	}
	printf("%d",ans);
	return 0;
}
时间: 2024-11-05 12:16:38

【BZOJ1283/3550】序列/[ONTAK2010]Vacation 最大费用流的相关文章

【bzoj1283】序列 线性规划与费用流

题目描述 给出一个长度为 的正整数序列Ci,求一个子序列,使得原序列中任意长度为 的子串中被选出的元素不超过K(K,M<=100) 个,并且选出的元素之和最大. 输入 第1行三个数N,m,k. 接下来N行,每行一个字符串表示Ci. 输出 最大和. 样例输入 10 5 3 4 4 4 6 6 6 6 6 4 4 样例输出 30 题解 线性规划与费用流 关于线性规划与费用流的具体讲解参见 bzoj1061 . 这道题和那道差不多,都是给出一大堆限制条件,每个变量在限制条件中的出现是连续的. 所以我们

BZOJ 1283 序列 费用流

题目大意:给定一个长度为n的序列,要求选一些数,使得任意一个长度为m个区间中最多选k个数,求最大的和 费用流直接跑就是了 把这个序列用流量为k费用为0的边连成一条直线 然后第i个点向第i+m个点连一条费用为a[i]流量为1的边 跑最大费用最大流即可 卡单纯型差评.... #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 1010 #d

BZOJ3550: [ONTAK2010]Vacation

3550: [ONTAK2010]Vacation Time Limit: 10 Sec  Memory Limit: 96 MBSubmit: 91  Solved: 71[Submit][Status] Description 有3N个数,你需要选出一些数,首先保证任意长度为N的区间中选出的数的个数<=K个,其次要保证选出的数的个数最大. Input 第一行两个整数N,K.第二行有3N个整数. Output 一行一个整数表示答案. Sample Input 5 3 14 21 9 30 11

【BZOJ3502/2288】PA2012 Tanie linie/【POJ Challenge】生日礼物 堆+链表(模拟费用流)

[BZOJ3502]PA2012 Tanie linie Description n个数字,求不相交的总和最大的最多k个连续子序列. 1<= k<= N<= 1000000. Sample Input 5 2 7 -3 4 -9 5 Sample Output 13 题解:跟1150和2151差不多. 我们先做一些预处理,因为连续的正数和连续的负数一定是要么都选要么都不选,所以可以将它们合并成一个数,同时区间中的零以及左右两端的负数没有意义,可以将它们删掉.然后我们得到的序列就变成:正-

最长递增子序列问题(费用流).cpp

http://acm.nefu.edu.cn/JudgeOnline/problemShow.php?problem_id=487 费用流,每次沿着最长边增广 //http://www.cnblogs.com/IMGavin/ #include <iostream> #include <stdio.h> #include <cstdlib> #include <cstring> #include <queue> #include <vect

【BZOJ-3638&amp;3272&amp;3267&amp;3502】k-Maximum Subsequence Sum 费用流构图 + 线段树手动增广

3638: Cf172 k-Maximum Subsequence Sum Time Limit: 50 Sec  Memory Limit: 256 MBSubmit: 174  Solved: 92[Submit][Status][Discuss] Description 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少. Input The first line contains integer n (1 ≤ n 

HDU 4411 Arrest 费用流

题目链接:点击打开链接 题意: 给定n+1个点([0,n] )m条边的无向图.起点为0,k个人初始在起点, 去遍历图使得每个点至少被一人走过且遍历 i 点时 i-1 必须已经被遍历. 使得k人的路径和最小,最后k人要回到起点. 思路: 费用流,因为对于一个人来说,这个人遍历点的序列一定是一个递增序列(不需要连续) 所以建图时i的出点只需要连接i+? 的入点. 若建一个完全图则会因为spfa跑负环... #include<iostream> #include<stdio.h> #in

【网络流24题】No.21 (最长 k 可重区间集问题 最长不相交路径 最大费用流)

[] 输入文件示例input.txt4 21 76 87 109 13 输出文件示例output.txt15 [分析] 直接co题解好了,写得挺全.. [建模方法] 方法1 按左端点排序所有区间,把每个区间拆分看做两个顶点<i.a><i.b>,建立附加源S汇T,以及附加顶点S'. 1.连接S到S'一条容量为K,费用为0的有向边.2.从S'到每个<i.a>连接一条容量为1,费用为0的有向边.3.从每个<i.b>到T连接一条容量为1,费用为0的有向边.4.从每个

POJ 2677 旅行商问题 双调dp或者费用流

Tour Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 3408   Accepted: 1513 Description John Doe, a skilled pilot, enjoys traveling. While on vacation, he rents a small plane and starts visiting beautiful places. To save money, John must