TYZ 8/25 ranwen的烦恼

冉文的烦恼

【问题描述】

冉文是个粗心的人,经常不小心把垃圾丢到地上。艾教实在没办法,只好派了m个学生,去捡冉文走过路上的垃圾。

假设冉文走过的路线是一条直线,每个位置分别是1..n,每个位置上垃圾的个数是ai。所有学生起始位置都在0,每秒钟,他们有两种选择:

1.向右走一步。

2.捡起一个地上的垃圾。

现在我们的问题是:要捡起冉文的所有垃圾,最少需要多少秒?

【输入格式】

第一行输入两个正整数n,m,如题目所述。

第二行输入n个非负整数ai,代表每个位置的垃圾个数。ai<=10^9

【输出格式】

一个数字,表示最少的时间。

【输入样例】

3 2

1 0 2

【输出样例】

5

解释:第一个学生走到3,然后捡2个垃圾,共花费时间5,第二个学生走到1,捡1个垃圾,共花费时间2。

【数据范围与约定】

对于30%的数据,m=1。

对于另30%的数据,m=2,垃圾总个数<=21。

对于100%的数据,n,m<=100000




题目的解答:

考试的时候不在乎你AC了多少题,只在乎你拿到了多少该拿的分数

30% m= =1的数据啊,直接暴力计算一下时间就好了,刚刚接触算法的人也会

30% m==2 的,一看到21,老司机们都知道这是要用dfs开车了

我考试的时候用dfs,但是注意这是辣鸡的总数21,我的dfs是按照光标位置搜索的,当n极大的时候(中间是铺天盖地的0),挂掉一个

再来说正解

记得不记得noip2015的挑石头,对于ans进行二分,没错这就是要做的事情

每个二分进行暴力的模拟,确定是否可行

从后往回模拟



#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
int n,m,k;
const ll maxn=100005;
ll a[maxn];
ll ans;
const ll inf=0x3f3f3f3f;
ll b[maxn];
ll c[maxn];
ll calb()
{
	int rescur=-1;
	for(int i=n;i>=1;i--)
	{
		if(b[i])
		{
			rescur=i;break;
		}
	}
	if(rescur==-1)
	return 0;
	int ressum=0;
	ressum+=rescur;
	for(int i=1;i<=rescur;i++)
	{
		ressum+=b[i];
	}
	return ressum;
}
ll cala()
{
	int rescur=-1;
	for(int i=n;i>=1;i--)
	{
		if(a[i])
		{
			rescur=i;break;
		}
	}
	if(rescur==-1)
	return 0;
	int ressum=0;
	ressum+=rescur;
	for(int i=1;i<=rescur;i++)
	{
		ressum+=a[i];
	}
	return ressum;
}
int lm;
vector<int> q;
bool check(ll t)
{
	 ll nm=m,extra=0;
	 for(int i=1;i<=n;i++)
	 {
	 	if(nm<a[i]/t)
	 	return 0;
	 	//如果人手不够,直接乱棍打死 
	 	else
	 	nm-=a[i]/t;//垃圾多于总时间,就得将记得伙计交代在这里 
	 	int left_task=a[i]%t;
	 	if(extra>=left_task)
	 	//壮劳力伤不起啊,剩余的时间叫做extra,可以用他干点别的 
	 	extra-=left_task;
	 	else
	 	{//当时如果被掏空了,就得重新找个伙计了 
	 		if(!nm)
	 		return 0;
	 		else
	 		{
	 			nm--,extra+=t-left_task;
			 }
		 }
		 t--;
		 if(!t)
		 {
		 	for(int j=i+1;j<=n;j++)
		 	if(a[j])
		 	return 0;
		 	//时间已经到了如果还有
			 //辣鸡你就死定了 
		 }
		 if(extra) extra--;
		 //走路也是需要时间的! 
	 }
	 return 1;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	a[0]=inf;
	for(int i=n;i>=0;i--)
	if(a[i]!=0)
	{
		n=i;
		break;
	}
	if(!n)
	{
		cout<<0<<endl;
		return 0;
	}
	if(m==1)
	{
		ans+=n;
		for(int i=1;i<=n;i++)
		ans+=a[i];
		cout<<ans<<endl;
		return 0;
	}
	if(m==2)
	{	
	ans=inf;
	    for(int i=1;i<=n;i++)
	    {	if(a[i])
	    	for(int j=1;j<=a[i];j++)
		    q.push_back(i);
	    }
		int lm=q.size();
		int _limit=1<<lm;
		for(int i=0;i<(_limit);i++)
		{
		  for(int j=0;j<lm;j++)
		  {
		  	if(i&(1<<j))
		  	{
		  		int xq=q[j];
		  		a[xq]--;
		  		b[xq]++;
			  }
		  }	
		  //二进制枚举所有的状况,a可以带多少,随便带 
		  ll nw=max(cala(),calb());
		  ans=min(ans,nw);
		  for(int j=0;j<lm;j++)
		  {
		  	if(i&(1<<j))
		  	{
		  		int xq=q[j];
		  		a[xq]++;
		  		b[xq]--;
			  }
			  //回溯 
		  }	
		}
	cout<<ans<<endl;
	return 0;
    }
    else
    {
   ll r=2000000000000000,l=0;
   while(r-l>1)
   {
   	ll mid=(l+r)/2;
   	bool nw=check(mid);
   	if(nw)
   	r=mid;
   	else
   	l=mid;
	} 
	cout<<l<<endl;
	//二分,不说了吧? 
	}
}



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值