2017.7.15 NOIP模拟
来源:互联网 发布:法棍 知乎 编辑:程序博客网 时间:2024/06/06 22:45
全国信息学分区联赛模拟试题(七)
【试题概览】
1.塔
【题目描述】
给出 N 个木块,告诉你每块木块的高度,你要用这些木块搭出两座高度相同的塔,一座塔的高度为搭建它的木块的
高度和,并且一座塔至少要用一块木头。木块只能用一次,也可以不用。问在两座塔的高度相同的限制下,能够搭
的塔的最大高度是多少?
【输入文件】
第一行一个整数 N,表示木块个数;
第二行 N 个整数,表示 N 块木块的高度。
【输出文件】
仅一个数,表示能搭建的最高的塔的高度,若不不能搭建两座相同高度的塔,输出-1。
【样例输入】
3\
2 3 5
【样例输出】
5
【数据规模】
N<=50,每块木块的高度范围[1,500000],所有木块的高度总和<=500000。
题解
由于高度范围到500000\
所以显开一个dp[500000][5000000]\
描述能否达到左塔为i右塔为j的 然后扫描dp[i][i]是不行的\
所以要改变dp的方式为==差值dp==\
dp[i][j]表示
前i个木块使两个塔的高度差为j时高塔的高度的最大值
所以可以由四种状态转移而得来
1.对于木块i不放
2.放在矮的上面但是高度不超过高的
3.放在高的上面
4.放在矮的上面但是超过了高塔的高度
这样得到转移方程 dp 输出dp[n][0]就行
注意 : 因为只有dp[0][0]是合法的所以dp[0][0]=0;
其他的为-inf
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<queue>#include<cmath>#define inf 0x7fffffusing namespace std;int a[55];int dp[55][500005];int tot=0;int main(){// freopen("tower.in","r",stdin);// freopen("tower.out","w",stdout); int n; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); tot+=a[i]; } //printf("tot:%d\n",tot); for(int i=0;i<=n;i++) for(int j=0;j<=tot;j++) dp[i][j]=-inf; dp[0][0]=0; for(int i=1;i<=n;i++){ //for(int j=0;j<=tot;j++) dp[i][j]=dp[i-1][j]; for(int j=0;j<=tot;j++) { dp[i][j]=max(dp[i-1][j],dp[i-1][j+a[i]]); if(j>=a[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-a[i]]+a[i]); else dp[i][j]=max(dp[i][j],dp[i-1][a[i]-j]+j); //dp[i][j+a[i]]=max(dp[i-1][j+a[i]],dp[i-1][j]+a[i]); //if(j<=a[i]) dp[i][a[i]-j]=max(dp[i][a[i]-j],dp[i-1][j]+a[i]-j); // else dp[i][j-a[i]]=max(dp[i][j-a[i]],dp[i-1][j]); } } if(dp[n][0]>0) printf("%d",dp[n][0]); else printf("-1"); return 0; }
2.圆
【题目描述】
给出 N 个圆,保证任意两个圆都是相离的,然后给出两个点(x1,y1)、(x2,y2),保证均不在某个圆上,要从(x1,y1)到
(x2,y2)画条曲线,问这条曲线最少要穿过多少次圆的边界?
【输入文件】
第一行一个整数 N,表示圆的个数;
第二行 N 个整数,表示 N 个圆的 X 坐标;
第三行 N 个整数,表示 N 个圆的 Y 坐标;
第四行 N 个整数,表示 N 个圆的半径 R;
第五行四个整数 x1,y1,x2,y2。
【输出文件】
仅一个数,表示最少要穿过多少次圆的边界。
【样例输入 1】
1\
0\
0\
2\
-5 1 5 1
【样例输出 1】
0
【样例输入 2】
7\
1 -3 2 5 -4 12 12\
1 -1 2 5 5 1 1\
8 1 2 1 1 1 2\
-5 1 12 1
【样例输出 2】
3
【数据规模】
1<=N<=50,坐标范围[-1000,1000],每个圆的半径 1<=R<=1000。
保证没有两个圆有公共点,起点和终点不会落在某个圆的边界上。
题解
找x1,y1和x2,y2分别在几个圆里面\
如果都在一个圆里面 就不算\
输出cnt1+cnt2就行了
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>#define X 1001using namespace std;int ctx1=0,ctx2=0;int x;int y;int x2;int y2;struct node{ int x,y,r;}c[55];bool incircle(int x,int y,int a,int b,int r){ int t1=x-a;int t2=y-b; if(t1*t1+t2*t2<=r*r) return 1; return 0;}bool flagx1=0;bool flagx2=0;/*void rep(int x,int y,int a,int b){ if(2*y-b<=2001&&2*y-b!=b) mp[a][2*y-b]++; if(2*x-a<=2001&&2*x-a!=a) mp[2*x-a][b]++; if(2*y-b<=2001&&2*x-a<=2001&&2*x-a!=a&&2*y-b!=b) mp[2*x-a][2*y-b]++;}*/void solve(int p){ int tx=c[p].x,ty=c[p].y,tr=c[p].r; flagx1=0;flagx2=0; if(incircle(tx,ty,x,y,tr)) flagx1=1; if(incircle(tx,ty,x2,y2,tr)) flagx2=1; if (flagx1&&flagx2) return ; else{ if(flagx1) ctx1++; else if(flagx2) ctx2++; } //printf("mp: %d",mp[996][1002]);}int main(){ freopen("circle.in","r",stdin); freopen("circle.out","w",stdout); int n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&c[i].x); for(int i=1;i<=n;i++) scanf("%d",&c[i].y); for(int i=1;i<=n;i++) scanf("%d",&c[i].r); for(int i=1;i<=n;i++) { c[i].x+=X;c[i].y+=X; } scanf("%d%d%d%d",&x,&y,&x2,&y2); x+=X;x2+=X;y+=X;y2+=X; if(x==x2&&y==y2) { printf("0"); return 0; } for(int i=1;i<=n;i++) solve(i); //printf("%d %d",ctx1,ctx2); printf("%d",ctx1+ctx2); return 0;}
3.猴子
【题目描述】
有 N 只猴子,第一只尾巴挂在树上,剩下的 N-1 只,要么被其它的猴子抓住,要么抓住了其它的猴子,要么两者均
有。当然一只猴子最多抓两只另外的猴子,只有两只手嘛。现在给出这 N 只猴子抓与被抓的信息,并且在某个时刻
可能某只猴子会放掉左手或右手的猴子,导致某些猴子落在地上。求每只猴子落地的时间。
【输入文件】
第一行两个数 N、M,表示有 N 只猴子,并且总时间为 M-1。
接下来 N 行,描述了每只猴子的信息,每行两个数,分别表示这只猴子左手和右手抓的猴子的编号,如果是-1,表
示该猴子那只手没抓其它的猴子。再接下来 M 行,按时间顺序给出了一些猴子放手的信息,第 1+N+i 行表示第 i-1
时刻某只猴子的放手信息,信息以两个数给出,前者表示放手的猴子的编号,后者表示其放的哪只手,1 表示左手 ,
2 表示右手。
【输出文件】
共 N 行,第 i 行表示第 i 只猴子掉落的时刻,若第 i 只猴子到 M-1 时刻以后还没掉落,就输出-1。
【样例输入】
3 2\
-1 3\
3 -1\
1 2\
1 2\
3 1
【样例输出】
-1\
1\
1
【数据规模】
30%的数据,N<=1000,M<=1000;
100%的数据,1<=N<=200000,1<=M<=400000。
题解
比较难的一题\
要反过来思考\
首先求出m-1秒后猴子的情况\
然后反过来 ==把从猴子上掉下来看为从地上接上去==
那么如果接到了一个有1的连通块
那么就是这个时候从1这个连通块上面掉下来的
这样 此题就变成了一个简单的并查集的题目\
每次用并查集维护连通块 记录时间即可
关于为什么要重新在输出的时候findf一遍的原因
因为在之前的findf只是把要松手的猴子findf一遍
比如说
5 22 -13 -14 -15 -1-1 -11 14 5
这组数据就是一条链\
然后1 2放手 4 5放手\
把3 这个点夹在了中间
但是没有最后一遍的findf 3的时间就是找不到的
所以要多findf一遍
#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>#include<cmath>#include<queue>using namespace std;int a[2000005][3];int fa[2000005],time[2000005];bool mk[2000005][3];struct node{ int x,y;}e[400005];int findf(int x){ if(fa[x]==x) return x; else{ int tmp=fa[x]; fa[x]=findf(fa[x]); if(time[tmp]<time[x]) time[x]=time[tmp]; return fa[x]; }}void merge(int x,int y,int t){ if(x==1) { fa[y]=x; time[y]=t; } else if(y==1){ fa[x]=y; time[x]=t; } else fa[x]=y;}int main(){ freopen("monkey.in","r",stdin); freopen("money.out","w",stdout); int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d%d",&a[i][1],&a[i][2]); int x,y; for(int i=0;i<=m-1;i++){ scanf("%d%d",&x,&y); mk[x][y]=1; e[i].x=x;e[i].y=y; } memset(time,127,sizeof(time)); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=n;i++) for(int j=1;j<=2;j++){ if(!mk[i][j]&&a[i][j]!=-1) { int x=findf(i),y=findf(a[i][j]); if(x!=y) merge(x,y,m); } }//先处理m-1秒之后的情况 没有断的先连在一起 for(int i=m-1;i>=0;i--){ if(a[e[i].x][e[i].y]!=-1){ int x=findf(e[i].x); int y=findf(a[e[i].x][e[i].y]); if(x!=y) merge(x,y,i); } } for(int i=1;i<=n;i++) { findf(i);//重新dfs 很关键 if(time[i]>=m) printf("-1\n"); else printf("%d\n",time[i]); } return 0; }
4.山
【题目描述】
给一座山,如图所示\
省略图片…\
现在要在山上的某个部位装一盏灯,使得这座山的任何一个部位都能够被看到。给出最小的 y 坐标,如图的+号处
就是 y 坐标最小的安装灯的地方。
【输入文件】
第一行一个数 N,表示这座山有 N 个点构成,接下来 N 行从左到右给出了这座山的构造情况,每行两个数 Xi,Yi ,
表示一个折点,保证 Xi>Xi-1(1
【输出文件】
仅输出一行,为最小的 y 坐标,当你的答案与标准答案相差 0.01 时,则被认为是正确的。
【样例输入】
6\
0 0\
10 0\
11 1\
15 1\
16 0\
25 0\
【样例输出】
3.00
【数据规模】
30%的数据,1<=N<=50;\
100%的数据,1<=N<=5000,0<=Xi,Yi<=100000,保证答案不超过 1000000。
题解
对于每个直线 算出解析式
因为要求的是高度 那么二分一个高度值
对于一个直线
f(x)=kx+b
那么这个答案点是要满足在所有直线所夹之间的(平行的线就在上方)
可以二分答案
所以把每个二分的答案
代入算一个x的取值范围:从l到r\
如果满足l
判断直线的k值
如果k>0 说明可以更新一个有端点
k==0 就判断y值和y=kx+b中的b 如果b大于y 则不合法
k<0 同理更新左端点
那么y可以向小的尝试(r=mid)\
否则(l=mid)
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>#include<queue>#define eps 1e-8#define inf 0x7ffffffusing namespace std;struct node{ double k,b;}line[15005];int n;double x[15005],y[15005];bool check(double y){ double l=-inf,r=inf; for(int i=1;i<=n-1;i++){ if(line[i].k>0){ double xx=(y-line[i].b)/line[i].k; r=min(xx,r); } if(line[i].k==0&&line[i].b>y) return false; if(line[i].k<0){ double xx=(y-line[i].b)/line[i].k; l=max(xx,l); } if(l>r) return false; } return true;}int main(){ //freopen("hill.in","r",stdin); //freopen("hill.out","w",stdout); scanf("%d",&n); double l=0,r=1000000; double ans=0; scanf("%lf%lf",&x[1],&y[1]); for(int i=2;i<=n;i++){ scanf("%lf%lf",&x[i],&y[i]); double disy=(y[i]-y[i-1]);double disx=(x[i]-x[i-1]); double k=disy/disx; double b=y[i]-k*x[i]; line[i-1].k=k;line[i-1].b=b; } while(l+0.001<r){ double mid=(l+r)/2; if(check(mid)){ ans=r=mid; } else l=mid; } printf("%lf",ans); return 0; }
- 2017.7.15 NOIP模拟
- [noip模拟2017.7.3]
- 2017.7 13 NOIP模拟赛
- 【集训】jzoj 2017.7.15 noip模拟赛A 总结
- Noip模拟
- 【NOIP模拟】20151004模拟
- 【NOIP模拟】 20151005模拟
- 【NOIP模拟】 20151006模拟
- 【NOIP模拟】 20151007模拟
- 【NOIP模拟】20151014模拟
- 【NOIP模拟】20151015模拟
- 2017.10.15 noip模拟赛 总结
- 【09 NOIP 模拟】light
- [NOIP模拟]Day1
- 8.9CH NOIP模拟
- 8.10FCH NOIP模拟
- 8.13NOIP模拟
- 8.14NOIP模拟
- jupyter 多个python版本
- 通过定时器制作的进度条
- HDU-搬寝室
- 【Spring】——聊一聊AOP
- BOOST UDP 网络通信(1)
- 2017.7.15 NOIP模拟
- UVa 11427 Expect the Expected
- Git配置及使用
- WUST 1944 最短网络Agri-Net(最小生成树之prim算法)
- node.js---study1 实现一个简单应用,登录,session
- [Microsoft] Search a 2D Matrix
- JDK 1.5-1.8特性
- 基于NRF51822实现触摸按键方案
- Linux 文件基本属性以及操作技巧