校内互测 选数字

选数字
2.1 题目描述Description
jn 在你的帮助下完成数学作业后, 又向你提出了一个苦思冥想但仍然不会做的问题。 给出一
个长度为 3n 的数列,规定相邻的 n 个数只能选 k 个,最大化所选数的和
2.2 输入Input
从 num.in 输入第一行有两个正整数 n,k,第二行有 3n 个正整数,即
i
a
2.3 输出Output
输出到 num.out,输出为最大的满足条件的数字之和
2.4 样例输入SampleInput
5 3
14 21 9 30 11 8 1 20 29 23 17 27 7 8 35
2.5 样例输出SampleOutput
195
2.6 数据范围Hint
对于 20%的数据,n≤20
对于 60%的数据,n≤40
对于 20%的数据,k=1
对于 20%的数据,a[i] ≤100

对于 100%的数据,n≤200,k≤10,0≤a[i]<=10^6以上数据有交集


题解:这道题我写的最大费用最大流。刚开始是按照k==1的情况思考的,想拿些部分分,结果A了。

先说怎么见图吧。

首先拆点,拆成的两个点之间连一条容量为1,价值为a[i]的边。然后从源点向每个点连一条容量为1,价值为0的边,

从每个点向汇点连一条容量为1,价值为0的边。因为相邻的n各点只能取k个,那么我们强制所选的每一条路径上的点都是属于不同的快,即两点之间的距离至少为n,就是从i点向i+n和之后的点连边,容量为1,价值为0。最后,把汇点拆开,连边容量为K,价值为0.  这样我们就会最多选择K条路径,而路径上的点都分属不同的快,这样的话每个快最多选取K个点,即保证了正确性。如果还是不懂的话,就只能感性的理解一下了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int n,k,a[1000],maxn,dis[100000],laste[100000],can[100000];
int next[2000000],point[100000],v[2000000],remain[2000000],cost[2000000],tot=-1;
int const inf=1e9;
void add(int x,int y,int z,int k)
{
  tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z; cost[tot]=k;
  tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0; cost[tot]=-k;
}
int addflow(int s,int t)
{
  int ans=inf,now=t;
  while (now!=s)
   {
   	 ans=min(ans,remain[laste[now]]);
   	 now=v[laste[now]^1];
   }
  now=t;
  while (now!=s)
   {
   	 remain[laste[now]]-=ans;
   	 remain[laste[now]^1]+=ans;
   	 now=v[laste[now]^1];
   }
  return ans;
}
bool spfa(int s,int t)
{
    memset(dis,128,sizeof(dis));
	memset(can,0,sizeof(can));
	can[s]=1; 
	queue<int> p; p.push(s);  dis[s]=0;
	while (!p.empty())
	 {
	 	int now=p.front(); p.pop(); can[now]=0;
	 	for (int i=point[now];i!=-1;i=next[i])
	 	 if (dis[v[i]]<dis[now]+cost[i]&&remain[i])
	 	 {
	 	 	dis[v[i]]=dis[now]+cost[i];
	 	 	laste[v[i]]=i;
	 	 	if (!can[v[i]])
	 	 	 {
	 	 	 	can[v[i]]=1;
	 	 	 	p.push(v[i]);
	 	 	 }
	 	 }
	 }	
	if (dis[t]<0)  return 0;
	maxn+=addflow(s,t)*dis[t];
	return 1;
}
void maxflow(int s,int t)
{
	while (spfa(s,t));
}
int main()
{
  freopen("num.in","r",stdin);
  freopen("num.out","w",stdout);
  memset(next,-1,sizeof(next));
  memset(point,-1,sizeof(point));
  scanf("%d%d",&n,&k);
  for (int i=1;i<=3*n;i++)
   scanf("%d",&a[i]);
  for (int i=1;i<=3*n;i++)
   add(0,i,1,0);
  for (int i=1;i<=3*n;i++)
   add(i+3*n,6*n+1,1,0);
  for (int i=1;i<=3*n;i++)
   {
   	 add(i,i+3*n,1,a[i]);
   	 for (int j=i+n;j<=3*n;j++)
   	  add(i+3*n,j,1,0);
   }
   add(6*n+1,6*n+2,k,0);
   maxflow(0,6*n+2);
   printf("%d",maxn);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值