目录
A - Cow Acrobats
B - Dropping tests
C - K Best
D - Pyramid Split
E - Alignment
F - Longest Ordered Subsequence
G - 排序
A - Cow Acrobats
题意:
给n只奶牛,每只奶牛都有两个属性, 风险值(w[i])和抗压值(s[i])。
现在要把这些牛垒在一起, 每个牛都有一个危险值:位他上面的牛的风险值之和减去它的抗压值。
问最大危险值的最小值?
(n<=5e4,1 <= w[i]<= 1e4,1 <= s[i] <= 1,000,000,000 )
思路:
看似二分,但是没有好下手的点,但是可以用二分。
我们假设现在n头牛已经叠好罗汉,从上往下第i头牛与第(i+1)头牛的数据为:
(sum:前i-1头牛的风险值之和)
属性\牛 | 第i头牛 | 第i+1头牛 |
---|---|---|
风险值 | w[i] | w[i+1] |
抗压值 | s[i] | s[i+1] |
危险值 | (C1)sum-s[i] | (C2)sum+w[i]-s[i+1] |
现在我们交换两者的位置,交换位置之后只会对这两头牛产生影响:
属性\牛 | 第i头牛 | 第i+1头牛 |
---|---|---|
风险值 | w[i] | w[i+1] |
抗压值 | s[i] | s[i+1] |
危险值 | (C3)sum+w[i+1]-s[i] | (C4)sum-s[i+1] |
此时C3>C1,C4<C2。
我们对比C2与C3的值:
C2-C3== w[i]-s[i+1]-w[i+1]+s[i] == w[i]+s[i]-(w[i+1]+s[i+1])
此时我们发现,影响危险值变换的是w+s的值,所以我们进行贪心,按照w+s的值由小到大排序。
-
考察点:贪心,思维,递推,二分
代码:
///¿¨ÊäÈë
#include<algorithm>
#include<cstdio>
#include<iostream>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
struct node
{
ll weight,power;
}a[maxn];
bool cmp(node x,node y)
{
return x.power+x.weight<y.power+y.weight;
}
int main()
{
int n;
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i].weight>>a[i].power;
sort(a+1,a+1+n,cmp);
// for(int i=1;i<=n;i++)
// cout<<a[i].weight<<" "<<a[i].power<<endl;
ll ans=-inf,sum=0;
for(int i=1;i<=n;i++){
//cout<<sum<<" "<<a[i].power<<endl;
ans=max(sum-a[i].power,ans);
sum+=a[i].weight;
}
cout<<ans<<endl;
}
B - Dropping tests
题意:
给出长度为n的序列a、b,ai与bi为绑定关系。
现在你可以去掉k组(ai,bi),使得题目中的公式得到一个最大值,输出这个最大值。
思路:
二分答案的经典问题之一:最大化平均值。
我们转化一下公式:
接下来我们二分m的值即可。
-
考察点:二分
代码:
#include <algorithm>
#include <cstdio>
#include<iostream>
using namespace std;
struct node
{
double a,b;
}arr[1050];
double num[1050];
int main()
{
int n,k;
while(~scanf("%d%d",&n,&k))
{
if(n==0&&k==0)
break;
for(int i=1;i<=n;i++){
scanf("%lf",&arr[i].a);
}
for(int i=1;i<=n;i++){
scanf("%lf",&arr[i].b);
}
double l=0.0,r=1.0;
double mid;
while(r-l>1e-8){
mid=(l+r)/2*1.0;
for(int i=1;i<=n;i++)
num[i]=arr[i].a-mid*arr[i].b;
sort(num+1,num+1+n);
double sum=0;
for(int i=k+1;i<=n;i++){
sum+=num[i];
}
//printf("%f\n",sum);
if(sum>0)
l=mid;
else
r=mid;
}
printf("%.0f\n",mid*100);
}
return 0;
}
C - K Best
题意:
现在有n个珍珠,每个珍珠都有一个价值vi与重量wi。
我们把 价值和/重量和 称为珍珠们的性价比。
我现在要买k个珍珠,请问我能拿到的最大性价比是多少?
思路:
跟B题一样,最大化平均值。
还是先化简公式:
接下来还是二分m的值。
-
考察点:二分
代码:
///输入要用scanf/快读
#include <algorithm>
#include <cstdio>
#include<iostream>
using namespace std;
const int maxn=1e5+100;
struct node
{
int pos;
double p,v,w;
}a[maxn];
bool cmp(node x,node y)
{
return x.p>y.p;
}
int b[maxn];
int main()
{
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
a[i].pos=i;
scanf("%lf%lf",&a[i].v,&a[i].w);
}
double l=0,r=10000010;
while((r-l)>1e-8)
{
double mid=(l+r)/2;
for(int i=1;i<=n;i++)
a[i].p=a[i].v-a[i].w*mid;
sort(a+1,a+1+n,cmp);
double sum=0;
for(int i=1;i<=k;i++){
sum+=a[i].p;
b[i]=a[i].pos;
}
if(sum<0)
r=mid;
else
l=mid;
}
for(int i=1;i<=k;i++){
if(i!=1) printf(" ");
printf("%d",b[i]);
}
printf("\n");
}
D - Pyramid Split
题意:
给你n个四棱锥,一起放在一个平面上。
然后在高度为h的位置,用一个平行平面去切所有的四棱锥,得到上下两部分。
使上下两部分体积相等,求h的大小(取整数部分输出)。
思路:
首先我们要知道四棱锥的体积公式。
接下来我们二分这个h的值。
假设当前的高为h,算出每个四棱锥切割之后上半部分的体积和v,与总体积V进行比较。
1.v>V ==> h过小
2.v<V ==> h过大
3.v==V ==> h合适
-
考察点:二分,数学
代码:
#include <algorithm>
#include <cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
int a[maxn],b[maxn];
double sum;
int n;
bool check(double x)
{
double ans=0;
for(int i=1;i<=n;i++)
{
if(x>=a[i]) continue;
double bili=(double(a[i])-x)/a[i];
ans+=b[i]*b[i]*bili*bili*(double(a[i])-x);
}
if(ans>=sum)
return true;
else
return false;
}
int main()
{
int t;
cin>>t;
while(t--)
{
sum=0;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
{
cin>>b[i];
sum+=b[i]*b[i]*a[i]*1.0;
}
sum/=2;
double l=0,r=10000,mid;
while(r-l>1e-8){
mid=(l+r)/2;
if(check(mid))
l=mid;
else
r=mid;
}
cout<<(int)mid<<endl;
}
}
E - Alignment
题意:
现在有长为n的序列a。
求当处理之后的序列满足a1 < a2 < a3 < ... < a(i ) <=> a(i+1) > a(i+2) > .. a(n-1) >a(n)
请问最少要删除的数字个数。
思路:
与P1091 [NOIP2004 提高组] 合唱队形基本上是同一题了。
差距只在于一点:
- P1091中前后严格递增递减:1 2 3 4 3 2 1
- 这道题并不是严格递增递减的 :1 2 3 4 4 3 2 1
所以这题在算答案的时候稍有不同。
-
考察点:LIS,思维,动态规划
代码:
#include<iostream>
#include<map>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
double a[1005];
int lis1[1005],lis2[1005];
int main()
{
int n;
ios::sync_with_stdio(false);
while(cin>>n)
{
int ans=-1;
for(int i=1; i<=n; i++)
cin>>a[i];
///第一遍LIS,正序
for(int i=1; i<=n; i++)
{
lis1[i]=1;
for(int j=1; j<i; j++)
if(a[i]>a[j])
lis1[i]=max(lis1[i],lis1[j]+1);
}
///第二遍LIS,逆序
for(int i=n; i>=1; i--)
{
lis2[i]=1;
for(int j=n; j>i; j--)
if(a[i]>a[j])
lis2[i]=max(lis2[i],lis2[j]+1);
}
for(int i=1; i<=n; i++)
for(int j=i+1;j<=n;j++)
ans=max(ans,lis1[i]+lis2[j]);
///注意!!!!我们求得的是K,我们要的是n-k!!!
cout<<n-ans<<endl;
}
return 0;
}
F - Longest Ordered Subsequence
题意:
给出长度为n的数字序列a
输出它的LIS长度。
思路:
LIS(Longest Increasing Subsequence,最长上升子序列)模板题。
-
考察点:动态规划,二分,LIS
代码:
#include<algorithm>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
int a[1050];
int lis[1050];
int main()
{
int n;
while(cin>>n)
{
for(int i=1;i<=n;i++)
cin>>a[i];
lis[1]=a[1];
int len=1;
for(int i=2;i<=n;i++){
if(a[i]>lis[len])
lis[++len]=a[i];
else{
int pos=lower_bound(lis+1,lis+1+len,a[i])-lis;
lis[pos]=a[i];
}
}
cout<<len<<endl;
}
}
G - 排序
题意:
中题你我?
思路:
没啥好说的,坑点有点多而已。
-
考察点:字符串,思维
-
坑点:两个5之间没有数字 、末尾没有5
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
ll a[maxn];
int main()
{
ios::sync_with_stdio(false);
string ss;
while(cin>>ss)
{
if(ss[ss.size()-1]!='5')
ss+='5';
int cnt=0,len=0;
ll ans=0;
for(int i=0; i<ss.size(); i++)
{
if(ss[i]=='5')
{
if(len)
{
a[cnt++]=ans;
ans=0;
len=0;
}
}
else
{
len++;
ans=ans*10+ss[i]-'0';
}
}
sort(a,a+cnt);
for(int i=0; i<cnt; i++)
{
if(i)
cout<<" ";
cout<<a[i];
}
cout<<endl;
}
return 0;
}