很不意外得考得不好呀。。。总结一下吧
A.挡光线
问题描述
坐标原点处有一光源,向第一象限发射光,光线成发散状,边缘处用OA,OB两条射线来表示。现在给你n个正方形,请你用它们将光线全部挡住,正方形的位置可以任意调整,但正方形的边必须平行于坐标轴,如下图所示。现在要求挡住后阴影部分的面积要尽可能大,请求出这个最大面积。
这个图片是用来麻人的
输入格式
第一行,一个整数N,表示正方形的个数。
第二行,四个实数,Xa,Ya,Xb,Yb,(Xa,Ya)表示点A的坐标,(Xb,Yb)表示点B的坐标
第三行,N个空格间隔的实数,分别表示N个正方形的边长。
输出格式
一个实数,表示所求结果,保留3个小数位
数据范围
对于100%的数据,0
题解:
正方形的对角线在一条直线时容易知道此时的面积最大,建系计算两个直线斜率,而剩余面积用三角形面积减去所有正方形面积和的一半即可。解释见代码。
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int main(){
double s=0,k=0,k1,k2,x,xa,len,sum=0,xb,ya,yb;
int n,i;
scanf("%d",&n);
scanf("%lf%lf%lf%lf",&xa,&ya,&xb,&yb);
k1=ya/xa;
k2=yb/xb;//算斜率
if(k1<k2)swap(k1,k2);
for(i=1;i<=n;i++){
scanf("%lf",&len);
s+=len*len/2;//所有正方形面积的一半
sum+=len;//所有正方形边长和
}
x=sum*(k2+1)/(k1-k2);//x为k较大的射线上正方形顶点的横坐标
printf("%.3lf",sum*(x*k1+(x+sum)*k2)/2+x*x*k1/2-s-(x+sum)*k2*(x+sum)/2);//用大面积减去小面积可解出答案
}
B.彩色方块
问题描述
何老板最近在玩一款叫“彩色方块”的小游戏,游戏虽然简单,但何老板仍旧乐此不疲。
游戏中有n个彩色方块成一排,方块的颜色用字母表示,给出目标排列,只要把它们排成跟目标一样的排列,就算过关。每次操作只能交换相邻两个方块。
给出一关游戏,何老板想知道,最少操作几次就能过关,请你帮他计算最少所需的操作步数。扫描二维码关注公众号,回复: 1565751 查看本文章
输入格式
第一行,一个整数n表示方块的数量
第二行,n个大写字母,表示一开始方块的排列情况。每个字母表示对应方块的颜色。
第三行,n个大写字母,表示方块的目标排列情况。
输出格式
一行,一个整数,表示计算结果。
数据保证有解。
数据范围
对于100%的数据:2<=n<=1,000,000
当时看到了以为是字符串算法。。技艺不精呀。
题解:
我们将原序列的每个字母从左至右标上序号,然后再将目标序列根据原序列一一对应的字母标上同样的号:
AACB->BACA
1 2 3 4->4 1 3 2(更优)或是4 2 3 1
容易得到比较近的更优,所以使用队列储存每种字母出现的顺序相对应的编号,然后将问题转化为求逆序对。
代码:
#include<iostream>
#include<cstdio>
#include<queue>
#define LL long long
using namespace std;
const int maxn=1000010;
queue <int> q[120];
int n,tree[maxn];
char a[maxn];
int p[maxn];
char read(){
char c=getchar();
while(c<'A'||c>'Z')c=getchar();
return c;
}
int lowbit(int x){
return x&(-x);
}
int getsum(int x){
int sum=0;
for(;x>0;x-=lowbit(x))sum+=tree[x];
return sum;
}
void modify(int x){
for(;x<=n;x+=lowbit(x))tree[x]++;
}
int main(){
int i,j;LL ans=0;
char x;
scanf("%d",&n);
for(i=1;i<=n;i++)a[i]=read();
for(i=1;i<=n;i++){
x=read();
q[x].push(i);//存入队列
}
for(i=1;i<=n;i++){
p[q[a[i]].front()]=i;//更新目标序列
q[a[i]].pop();
}
for(i=1;i<=n;i++){
ans+=getsum(n)-getsum(p[i]);
modify(p[i]);
}//使用树状数组计算逆序对个数
printf("%lld",ans);
return 0;
}
C.营养午餐
问题描述
信竞班的同学们拼命学习,编程刷题到了废寝忘食的地步,看着大家日渐消瘦,这让何老板很是担心。于是何老板打算给大家提供营养午餐。
何老板问同学们:“大家中午想吃什么呀?”
A同学:”牛排、意大利面、面包、水果汁”;
B同学:”酸辣粉、薯片、牛肉干、回锅肉、小虾龙、葡萄干、烤鱼、二锅头”
……
信竞班总共n个同学,每个同学都提出了自己必吃的若干种食物,何老板统计了一下,全班提出的食物总共有m种(编号1到m)。
受工资的影响,何老板最多只能提供其中k种食物,于是何老板想选出一些同学出来给他们提供午餐,要求选出来的这些同学所需的食物种类不超过k种。仁慈的何老板想让尽可能多的同学吃上午餐,问,最多能选出多少个同学出来?
输入格式
第一行,三个整数n、m和k
接下来n行,其中第i行描述第i名同学的食物需求情况,每行第1个数字t,表示该同学所需食物的种类数,接下来t个整数,表示对应食物的编号。
输出格式
一行,一个整数,表示最多能选出的同学的数量。
数据范围
对于100%的数据:
1 <=k<= m <= 15
1 <= n <= 1,000
题解:
我最开始看到数据范围就懵逼了———状压递推怎么做???
首先肯定是用食物集合状压,但是递推求集合的交集让我推了很久很久。。没做出
俗话说:敢暴力就敢A,没想到的是正解如此暴力但是却很有道理。。。
首先可以肯定的是,分发了k种食物的情况一定是最优的解。。
然后暴力枚举每位同学。。只要能满足他的需求ans++即可(每位同学的需求状压起来)
这时题目的复杂度为
代码:
//哇,真的皮。我还以为暴力过不了呢。想了1个多小时的正解。。。。。。
#include<iostream>
#include<cstdio>
using namespace std;
int f[1<<16],ma[1010],n,m,k;
int lowbit(int x){
return x&(-x);
}
bool pan(int x){//判断是否刚好有k种食物
int tim=0;
while(x){
x-=lowbit(x);
tim++;
}
if(tim==k)return true;
return false;
}
int main(){
int i,j,p,x,maxn,ans=0;
scanf("%d%d%d",&n,&m,&k);
for(i=1;i<=n;i++){
scanf("%d",&p);
for(j=1;j<=p;j++){
scanf("%d",&x);
ma[i]+=1<<(x-1);//将同学的需求状压起来
}
}
maxn=(1<<m)-1;
for(i=1;i<=maxn;i++)if(pan(i))//如果刚好有k种食品才开始计算,否则严重超时——O(2^m*n)
for(j=1;j<=n;j++)
if((i&ma[j])==ma[j])f[i]++;//只要满足他就ans++
for(i=1;i<=maxn;i++)if(pan(i))ans=max(ans,f[i]);
printf("%d",ans);
return 0;
}
D.nodgd参加APIO
题目背景:
nodgd正在APIO赛场上努力的A题。他按照以往的做法,先把所有题都看一遍,再选自己觉得最好做的题先做。nodgd觉得第二题是最容易上手的,这道题是这样描述的:一开始给你一个长度为N的非负整数序列,你需要把它砍断分成M段,那么这一分割方案的得分为……
给你这个序列以及正整数M,你需要求出最大的得分,并输出任意一个得到这个分数的方案。
看完题,nodgd又犯了老毛病,也就是考试的时候浮想联翩。他觉得这个题还不够有趣,于是决定修改一下题目。
题目描述:
将长度为N的非负整数序列分成M段,从左往右编号1到M,其中的编号为k的一段至少要有k个数,该段的得分为其中最大的一个数的值。总的得分就是这M个段的得分总和,请你求出最大的总分。
nodgd觉得这是一道不错的题目,于是就丧心病狂把它拿来当成高一同学的月赛题了……考虑到是高一同学,需要以鼓励和学习为主,所以nodgd把数据范围设置得比较小,并且不需要你输出方案。
输入格式
第一行两个正整数N,M,表示序列的长度以及你需要分割成的块数。
接下来N行每行一个数,为这个序列。
输出格式
只需要输出一个数,表示你能够获得的最大得分。
题解:
一看就知道是动归题目,状态f[i][j]表示前i项分为j段后的最大得分。
首先,枚举到第i个数时,我们可以把它加入前一段中,即为f[i-1][j]
或者将它单独分出成为单独一段。这时我们可以知道:这个数足够大,并且它显然是i-j的中最大的,所以这时f为f[i-j][j-1]+max(i-j+1,i),而max可以RMQ打表预处理。
时间复杂度:
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#define LL long long
using namespace std;
int fmaxx[200010][35],f[200010][25];
int getmaxx(int l,int r){
int k=floor(log2(r-l+1));
return max(fmaxx[l][k],fmaxx[r-(1<<k)+1][k]);//O(1)计算区间最值
}
int main(){
int k,i,j,n,m,p;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)scanf("%d",&fmaxx[i][0]);
k=ceil(log2(n));
for(i=1;i<=k;i++)
for(j=1;j<=n;j++)if((1<<i)<=n)
fmaxx[j][i]=max(fmaxx[j][i-1],fmaxx[j+(1<<i-1)][i-1]);//RMQ预处理
f[1][1]=fmaxx[1][0];
for(j=1;j<=m;j++){
for(i=j*(j+1)/2;i<=n;i++){
f[i][j]=max(f[i-1][j],f[i-j][j-1]+getmaxx(i-j+1,i));//动规
}
}
printf("%d",f[n][m]);
return 0;
}