zoukankan      html  css  js  c++  java
  • 聪明的质检员

    聪明的质检员

    https://www.luogu.com.cn/problem/P1314

    梳理题意:

    1. 给定矿石的数目n以及每个矿石的重量和价值;还定m个询问区间以及标准值s

    2. 要求对于每个区间,求出相应的Yi值;再求和算出总的Y值(W的值需要我们自己去枚举)

    • 计算公式:

    将计算Yi的公式翻译为代码实现,就是:在每一个区间中寻找价值大于W的矿石,找到一个就用tot累加(注意:只是累加个数),再用tott累加价值;最后Yi=tot×tott

    1. 我们的总任务是:找出一个W,使得对应的Y与s的绝对值最小。最后输出最小的|s-Y|

    做题历程:

    1. 读完题意,就想到暴力枚举:从0-n×m依次枚举W,在计算出对应的|s-Y|值,最后比较出最小输出即可。肯定会超时,但至少10分左右

    2. 但是交上去是0分。最开始是怀疑枚举的范围小了,就扩大范围枚举,还是0分

    3. 于是怀疑是将Yi的计算公式打错了。果然,前两次的代码将tot全部用作累加重量了!完全背离了题意。然后改了计算公式,交上去,有25分了,其他点全部超时。代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    long long n,m,s,maxn,sum=0x3f3f3f3f,l[200001],r[200001];
    struct node {
    	long long w,v;
    }a[200001];
    
    inline void solve(int x) {
    	long long y=0;
    	for(register int i=1;i<=m;i++) {
    		long long tot=0,tott=0;
    		for(register int j=l[i];j<=r[i];j++) {
    			if(a[j].w>=x) {
    				tot++;
    				tott+=a[j].v;
    			}
    		}
    		y=y+(tot*tott);
    	}
    	sum=min(sum,abs(y-s));
    }
    
    int main() {
    	scanf("%lld%lld%lld",&n,&m,&s);
    	for(register int i=1;i<=n;i++) {
    		scanf("%lld%lld",&a[i].w,&a[i].v);
    		maxn=max(maxn,a[i].w);
    	}
    	for(register int i=1;i<=m;i++) {
    		scanf("%lld%lld",&l[i],&r[i]);
    	}
    	for(register int i=0;i<=maxn;i++) {
    		solve(i);
    	}
    	printf("%lld",sum);
    	return 0;
    }
    
    1. 既然枚举有分,那就说明二分也能做且二分的得分会更高一些!那就将代码改为二分。二分代码,一般先直接套板子,再根据题意进行修改。交上去,有70分了!其他的还是超时问题。代码如下:
    #include <bits/stdc++.h>
    using namespace std;
    long long n,m,s,ans=1e15,maxn,l[200010],r[200010];
    struct node {
    	long long w,v;
    }a[200010];
    int main() {
    	scanf("%lld%lld%lld",&n,&m,&s);
    	for(register int i=1;i<=n;i++) {
    		scanf("%lld%lld",&a[i].w,&a[i].v);
    		maxn=max(maxn,a[i].w);
    	}
    	for(register int i=1;i<=m;i++) {
    		scanf("%lld%lld",&l[i],&r[i]);
    	}
    	long long L=0,R=maxn;
    	while(L<=R) {
    		long long y=0,mid=(L+R)>>1;
    		for(register int i=1;i<=m;i++) {
    			long long tot=0,tott=0;
    			for(register int j=l[i];j<=r[i];j++) {
    				if(a[j].w>=mid) {
    					tot++;
    					tott+=a[j].v;
    				}
    			}
    			y+=(tot*tott);
    		}
    		if((s-y)<0) L=mid+1;
    		else R=mid-1;
    		ans=min(ans,abs(s-y));
    	}
    	printf("%lld",ans);
    	return 0;
    }
    
    1. 那剩下的问题就只有优化了。打开算法标签,发现还有一个是前缀和!那优化肯定就是前缀和了!看题解,只要加入一个前缀和记录每一个mid值对应的所有矿石的前缀和,就可以优化时间复杂度了。那么对应到二分程序中,就是将tot以及tott改为数组,计算Y值时运用差分(前缀和)即可。再交上去,就AC了。代码如下:
    #include <bits/stdc++.h>
    using namespace std;
    long long n,m,s,ans=1e15,maxn,l[200010],r[200010],tot[200010],tott[200010];
    struct node {
    	long long w,v;
    }a[200010];
    int main() {
    	scanf("%lld%lld%lld",&n,&m,&s);
    	for(register int i=1;i<=n;i++) {
    		scanf("%lld%lld",&a[i].w,&a[i].v);
    		maxn=max(maxn,a[i].w);
    	}
    	for(register int i=1;i<=m;i++) {
    		scanf("%lld%lld",&l[i],&r[i]);
    	}
    	long long L=0,R=maxn;
    	while(L<=R) {
    		long long y=0,mid=(L+R)>>1;
    		for(register int i=1;i<=n;i++) {
    			if(a[i].w>=mid) {
    				tot[i]=tot[i-1]+1;
    				tott[i]=tott[i-1]+a[i].v;
    			}
    			else {
    				tot[i]=tot[i-1];
    				tott[i]=tott[i-1];
    			}
    		}
    		for(register int i=1;i<=m;i++) {
    			y+=(tot[r[i]]-tot[l[i]-1])*(tott[r[i]]-tott[l[i]-1]);
    		}
    		if((s-y)<0) L=mid+1;
    		else R=mid-1;
    		ans=min(ans,abs(s-y));
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

  • 相关阅读:
    iOS 8以后 定位手动授权问题
    IOS int NSInteger NSNumber区分
    Java基础知识(JAVA集合框架之List与Set)
    Java基础知识(JAVA基本数据类型包装类)
    Java基础知识(JAVA中String、StringBuffer、StringBuilder类的区别)
    Java基础知识(重载和覆盖)
    Java基础知识(抽象类和接口)
    host文件
    天猫页面显示错位
    专题8:javascript函数详解
  • 原文地址:https://www.cnblogs.com/Eleven-Qian-Shan/p/13067254.html
Copyright © 2011-2022 走看看