洛谷 1314 聪明的质监员
题意:数学公式,有n个产品 重量w 价值v,求m个区间里
(区间中 产品重量比W重的个数)*(区间中 产品重量比W重的产品价值的和)再将各个区间的yi加起来 就是Y。工厂的标准是s,找到一个W,使|Y-s|最小
分析:看到找到各种条件和与s最小,想到了二分,各个区间的和–前缀和。
前缀和
假设W,和所有产品使用yi,使用前缀和,找到各个位置W V方便求区间
[l,r]的和 == sum[r]-sum[l-1];(一定是l-1)
二分
我们很容易发现,W=0时,所有矿石都可以选上
当W大于最大重量时,一个都选不上
W值越大,选上的矿石越少,相反W值越小,选上的矿石越多
对应 W值越大,Y越小
所以 找到二分条件
Y>s 增大W来减小Y 减小|Y-s|
Y==s |Y-s|=0
Y<s 减小W来增大Y 减小|Y-s|
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
typedef pair<int,int>Pair;
const int N = 2e5+10;
int n,m;
long long s;
int w[N],v[N];
long long ww[N],vv[N];
long long ss;
vector<Pair>ve;
bool check(int W)
{
long long sum=0,l=ve.size();
memset(ww,0,sizeof(ww));
memset(vv,0,sizeof(vv));
for(int i=1;i<=n;i++)
{
if(w[i]>=W)ww[i]=ww[i-1]+1,vv[i]=vv[i-1]+v[i];
else ww[i]=ww[i-1],vv[i]=vv[i-1];
}
for(int i=0;i<l;i++)
{
sum+=(ww[ve[i].second]-ww[ve[i].first-1])*(vv[ve[i].second]-vv[ve[i].first-1]);
}
//cout<<W<<' '<<sum<<endl;
ss=fabs(sum-s);
if(sum>s)
//cout<<W<<' '<<ss<<endl;
return true;
else
return false;
}
int main()
{
int l=2147483647,r=-1,mid;
scanf("%d%d%lld",&n,&m,&s);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&w[i],&v[i]);
r=max(r,w[i]);
l=min(l,w[i]);
}
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
ve.push_back(Pair{a,b});
}
long long ans=0x3f3f3f3f3f3f3f3f;
while(l<r)
{
mid=(l+r+1)>>1;//不加+1 会死循环
//cout<<i<<' '<<j<<' '<<mid<<endl;
if(check(mid))l=mid;
else r=mid-1;
ans=min(ans,ss);
}
//printf("%d\n",l);
printf("%lld\n",ans);
return 0;
}
洛谷1281 书的复制
题意:有n种书,m个人,设计一种方案,一本书只能分给一个人,将n本书分配给m个人,前面尽量少分配,使得复制时间最短,复制时间为抄写页数最多的人用去的时间
分析:使用二分 判定性质:按照x 分配任务,从第一个任务开始分配,当sum>x 下一个人开始分配,分配完所有任务,需要的人s与m相比
我们容易发现 随着x的增大,需要的人s减少
所以
s>m 需要的人比m多 增大x 来减少s
s==m
s<m 需要的人比m少 减少x 来增大s
,找到最短最长的时间,按照时间去分任务,输出
#include<iostream>
#include<cstdio>
#include<string>
#include<cmath>
#include<vector>
using namespace std;
const int N = 600;
int n,m;
long long a[N];
int d[N][2],c=0;
bool check(int x)
{
int s=1;
long long sum=0;
for(int i=n-1;i>=0;i--)
{
sum+=a[i];
if(sum>x)
{
s++;
sum=a[i];
}
}
if(s>m)return false;
else return true;
}
void print(int x)//输出写的有点麻烦
{
int s=n-1,sum=0;
for(int i=n-1;i>=0;i--)
{
sum+=a[i];
if(sum>x)
{
//printf("%d %d\n",i+2,s+1);
d[c][0]=i+2;
d[c][1]=s+1;
c++;
sum=a[i];
s=i;
continue;
}
if(sum==x)
{
//printf("%d %d\n",i+1,s+1);
sum=0;
d[c][0]=i+1;
d[c][1]=s+1;
c++;
s=i-1;
continue;
}
if(i==0)
{
//printf("%d %d\n",i+1,s+1);
d[c][0]=i+1;
d[c][1]=s+1;
c++;
}
}
for(int i=c-1;i>=0;i--)
printf("%d %d\n",d[i][0],d[i][1]);
}
int main()
{
scanf("%d%d",&n,&m);
long long sum=0;
for(int i=0;i<n;i++)
scanf("%lld",&a[i]),sum+=a[i];
long long i=1,j=sum,mid;
while(i<j)
{
mid=(i+j)>>1;
//cout<<i<<'+'<<j<<' '<<mid<<endl;
if(check(mid))j=mid;
else i=mid+1;
}
print(i);
//printf("%d\n",i);
//cout<<s<<endl;
return 0;
}
洛谷1083 借教室
题意:有n天 每天有教室r间 ,目前有m个订单,每个订单 d间教室从s借到t,找到第一个不满足的订单
分析:二分答案 前缀和求解
二分
一共有m个订单
每天需要的教室c 随着订单的增加增加
c>t[i] 减少订单数来减少c 找到第一个不满足的订单
c=t[i[ |c-t[i]|=0
c<t[i] 增大订单数来增大c 可以完成更多的订单
前缀和
使用差分 构造完成前x个订单 每天需要的教师数
[l,r需要d个教室 sum[l]+=d;sum[r+1]-=d;
i(1<= i <=n)天需要的教室 c+=sum[i];
ps:有一种情况,所有订单都可以完成 开始将r设置为m+1
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e6+10;
int n,m;
long long t[N],tt[N];
struct Node{
long long d,s,t;//借教室数目d 开始s 到t
}p[N];
bool check(int x)
{
memset(tt,0,sizeof(tt));
for(int i=1;i<=x;i++)//差分构造一下 [l,r]+r 到x订单 每天需要的教室数目
{
tt[p[i].s]+=p[i].d;
tt[p[i].t+1]-=p[i].d;
}
int c=0;
for(int i=1;i<=n;i++)//现有教室是否多于目前需要
{
c+=tt[i];
//cout<<"i "<<i<<"c "<<c<<endl;
if(c>t[i])return false;
}
return true;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%lld",&t[i]);
for(int i=1;i<=m;i++)scanf("%lld%lld%lld",&p[i].d,&p[i].s,&p[i].t);
int i=1,j=m+1,mid;//二分查找答案 将j=m+1 若都可以满足,则i最终等于m+1
while(i<j)
{
mid=(i+j)>>1;
//cout<<i<<'+'<<j<<' '<<mid<<endl;
if(check(mid))i=mid+1;
else j=mid;
}
if(i!=m+1)
printf("-1\n%d\n",i);
else printf("0");
return 0;
}
/*
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
4 3
4 4 4 4
1 2 2
3 4 2
2 3 2
*/
题意:有n件衣服,每件衣服含水量a[i],有一个暖气片每分钟可以使一件衣服水分减少k,衣服放在外面 每分钟水分减少1,怎么设计策略使用最少的时间烘干所有衣服(所有衣服的水分小于等于0)
分析:使用二分来求解
贪心的思想 一共有t分钟 每分钟所有衣服减1,所以最后所有衣服先减去t时间,然后判断剩下的衣服能不能使用t个(k-1)烘干完成(k中已经减去了1)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 1e5+10;
long long a[N],n,k;
bool check(long long x)
{
long long s=x;
for(int i=n-1;i>=0;i--)
{
if(a[i]>x)
s-=(long long)ceil((double)(a[i]-x)/(k-1));
if(s<0)return false;
}
return true;
}
int main()
{
while(scanf("%lld",&n)!=EOF)
{
long long l=0,r=0,mid;
for(int i=0;i<n;i++)scanf("%lld",&a[i]),r=max(r,a[i]);
scanf("%lld", &k);
while(l<r)
{
mid=(l+r)>>1;
//cout<<l<<' '<<r<<' '<<mid<<endl;
if(check(mid))r=mid;
else l=mid+1;
}
printf("%lld\n", l);
}
return 0;
}