冉文的烦恼
【问题描述】
冉文是个粗心的人,经常不小心把垃圾丢到地上。艾教实在没办法,只好派了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;
//二分,不说了吧?
}
}