牛客网暑期ACM多校训练营(第五场)

咳咳。


  • A.gpa

题目链接:https://www.nowcoder.com/acm/contest/143/A

题意:给s和c数组,每个数组各有n个数,要求去掉k个s[i]和c[i],要尽量让大,求删掉k个后上述式子的最大值。

题解:第一次见到二分凑答案的题。

           我们假设>=d,移项后可以得出∑s[i](c[i]-d)>=0这个式子,我们令a[i] = s[i]*(c[i]-d),然后对a数组进行排序,那么a的前k个会较小,而后n-k项的和就是∑s[i](c[i]-d)>=0,这时候可以二分答案。记后n-k项和为sum,如果sum>=0,那么说明d偏小了,反之d偏大。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8

typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 200005;

inline void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

int n,k,s[maxn],c[maxn],Max,m;
double a[maxn];

bool judge()
{
	double sum = 0;
	for(int i=k;i<n;i++)
		sum += a[i];
	if(sum>=0)
		return true;
	return false;	
}

int main()
{
	read(n),read(k);
	for(int i=0;i<n;i++)
		read(s[i]);
	for(int i=0;i<n;i++)
	{
		read(c[i]);
		Max = max(c[i],Max);
	}
	double l=0,r = Max+2; //二分边界值也可以设成1000,r的最大值就是c[i]最大的那个,这个是根据∑s[i](c[i]-d)推出来的
	while(r-l>eps)
	{
		double mid = (l+r)/2;
		for(int i=0;i<n;i++)
			a[i] = (double)s[i]*((double)c[i]-mid);
		nth_element(a,a+k-1,a+n);
		if(judge())
			l = mid;
		else
			r = mid;
	}
	printf("%.11lf\n",l);
	return 0;
}

       


  • G.max

题目链接:https://www.nowcoder.com/acm/contest/143/G

题意:给一个数c和n,要求从1-n中找一对(a,b),使得其最大公约数为c,并尽量使a*b的结果大,问a*b最大是多少。

题解:

       以c为底不断地扩大a和b,假设最大能扩大x倍,x=n/c; 那么就把a变成c*n,b变成c*(n-1),由于gcd(c*n,c*(n-1)) = c*gcd(n,n-1) = c,所以就这么扩大了。当然特殊情况注意一下,比如说连扩大2倍都不行,a和b都得等于c。万一c>n,直接输出-1就行了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8

typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 200005;

inline void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

inline void print(int x)
{
    if(x<0){ putchar('-'); x=-x;}
    if(x>9) print(x/10);
    putchar(x%10+'0');
}

ll c,n,a,b;

int main()
{
	scanf("%lld%lld",&c,&n);
	a = c;
	b = c;
	int temp = n/c;
	b = temp*c;
	a = (temp-1)*c;
	if(temp==1)
		a = b =c;
	if(temp==0)
		cout<<-1<<endl;
	else
		cout<<a*b<<endl;
	return 0;
}

  • J. plan

题目链接:https://www.nowcoder.com/acm/contest/143/J

题意:有n个人,2人房价格为p1,3人房价格为p2,问最少花多少钱能把人全安顿下来。

题解:看上去像一个完全背包问题,但是它的n是1e9的数据规模,而房间种类只有两个,所以不是完全背包。

           一个值得注意的点就是,一个房子是可以不住满的。所以我在比赛时候想到的解法是:看全住3个人至少要多少间,假设结果为m,那么枚举[0,m],再算出此时要开的二人房的数量,枚举出最小费用。

           这种做法复杂度是O(n)的,本来打算最后一个点TLE的,结果评测机跑的贼快,居然过了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8

typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 200005;

ll n,p,q,ans;

int main()
{
	cin>>n>>p>>q;
	ll tot = n/3;
	if(n%3!=0)
		tot++;
	ans = tot*q;
	for(int i=0;i<tot;i++)
	{
		int temp = (n-i*3)/2;
		if((n-i*3)%2!=0) temp++;
		ans = min(ans,temp*p+i*q);
	}
	cout<<ans<<endl;
	return 0;
}

底下来看看O(1)的解法。

就是先判断2人和3人间哪个性价比比较高,如果2人间比较高的话,那么就全买两人间的,如果最后多一个人的话,可以考虑减去一个两人间,开一个三人间。。三人间的解法也类似,这里就不提了。直接上隔壁奥利奥队的代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=INT_MAX;
LL n,p2,p3;
int main() {
    scanf("%lld%lld%lld",&n,&p2,&p3);
    LL sum=0;
    if(3*p2<=2*p3){//二人间性价比高
        sum=n/2*p2;//先尽可能的全是二人间
        if(n%2==1 && n/2)  sum+=min(p2,p3-p2);//如果多了一个,且二人间多于1间,那就可以考虑
        //是加一个二人间还是减掉一个二人换一个三人
        else if (n%2)sum +=min(p2,p3);//考虑只有一个人的情况
    }
    else{
        sum=n/3*p3;
        if(n%3==2)  sum+=min(p2,p3);
        if(n%3==1&&n/3)  sum+=min(2*p2-p3,p2);//多了一个,可以少一个3人加两个2人,也可以直接加一个二人
        if(n%3==1&&n/3==0) sum+=p2;//只有一个人
    }
    printf("%lld\n",sum);
}

猜你喜欢

转载自blog.csdn.net/zxwsbg/article/details/81392596
今日推荐