全国信息学分区联赛模拟试题(七)
【试题概览】
试题名称 | 塔 | 圆 | 猴子 | 山 |
---|---|---|---|---|
提交文件 | tower | circle | monkey | hill |
输入文件 | tower.in | circle.in | monkey.in | hill.in |
输出文件 | tower.out | circle.out | monkey.out | hill.out |
时间限制 | 1s | 1s | 1s | 1s |
空间限制 | 128MB | 128MB | 128MB | 128MB |
题目来源 Topcoder Topcoder POIX COI2004
塔
题目描述
给出 N 个木块,告诉你每块木块的高度,你要用这些木块搭出两座高度相同的塔,一座塔的高度为搭建它的木块的 高度和,并且一座塔至少要用一块木头。木块只能用一次,也可以不用。问在两座塔的高度相同的限制下,能够搭 的塔的最大高度是多少? 【输入文件】 第一行一个整数 N,表示木块个数; 第二行 N 个整数,表示 N 块木块的高度。 ## 输出文件
仅一个数,表示能搭建的最高的塔的高度,若不不能搭建两座相同高度的塔,输出-1。
样例输入
3
2 3 5
样例输出
5
数据规模
N<=50,每块木块的高度范围[1,500000],所有木块的高度总和<=500000。
题解
又是一个杂技DP,好像叫什么差值DP,dp[i][j]表示利用前i个木块,搭出两个差为j的塔,其中最高的塔的较矮的那个高度,那么分三种情况讨论,
dp[i][j]=max(dp[i-1][j-h[i]],dp[i-1][h[i]-j]+h[i]-j,dp[i-1][j+h[i]]+h[i])
然后乱搞就行
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define LL long long
#define INF (1<<30)
using namespace std;
int dp[61][500006],n;
int h[100],sum;
int main(){
freopen("tower.in","r",stdin);
freopen("tower.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&h[i]),sum+=h[i];
for(int i=0;i<=n;i++)for(int j=0;j<=sum;j++)dp[i][j]=-INF;
dp[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=sum;j++){
dp[i][j]=dp[i-1][j];
if(j>=h[i])
dp[i][j]=max(dp[i][j],dp[i-1][j-h[i]]);
if(j<=h[i])
dp[i][j]=max(dp[i][j],dp[i-1][h[i]-j]+h[i]-j);
dp[i][j]=max(dp[i][j],dp[i-1][j+h[i]]+h[i]);
}
}
printf("%d",dp[n][0]);
}
/*
5
1 4 2 3 65
*/
圆
题目描述
给出 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。 保证没有两个圆有公共点,起点和终点不会落在某个圆的边界上。
题解
对于每一个圆,如果两个点一个在圆内,一个在圆外,那么一定要穿过这个圆,ans++即可
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
#define INF (1<<30)
using namespace std;
struct Point{
int x,y;
Point(int xx=0,int yy=0)
{x=xx;y=yy;}
}a,b;
struct Circle{
Point o;
int r;
Circle(Point oo=Point(0,0),int rr=0)
{o=oo;r=rr;}
}c[100];
int dis(Point A,Point B){
return (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y);
}
int n,ans;
int main(){
freopen("circle.in","r",stdin);
freopen("circle.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&c[i].o.x);
for(int i=1;i<=n;i++)
scanf("%d",&c[i].o.y);
for(int i=1;i<=n;i++)
scanf("%d",&c[i].r);
scanf("%d%d%d%d",&a.x,&a.y,&b.x,&b.y);
for(int i=1;i<=n;i++)
if(((LL)(dis(c[i].o,a)-c[i].r*c[i].r))*((dis(c[i].o,b)-c[i].r*c[i].r))<0ll)ans++;
printf("%d",ans);
}
/*
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
*/
猴子
题目描述
有 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。
题解
考虑时光倒流,那么猴子就是从地上一个个地跳上去,用并查集做就好了,本题可能隐藏并查集的高级用法,还有待挖掘
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
#define INF (1<<30)
using namespace std;
int n,m;
struct Edge{
int a,b;
}e[1000000];
int ch[200200][2];
int fa[200200];
void pre(){for(int i=1;i<=n;i++)fa[i]=i;}
int findf(int x){return fa[x]==x?x:fa[x]=findf(fa[x]);}
void mergef(int x,int y){fa[findf(x)]=findf(y);}
int to[1000000],nt[1000000],fp[200200],cnt=1;
void add(int x,int y){
to[cnt]=y;nt[cnt]=fp[x];
fp[x]=cnt++;
}
int ans[200200];
void dfs(int k,int t){
ans[k]=t;
for(int i=fp[k];i;i=nt[i]){
int kk=to[i];
if(ans[kk]!=-1)continue;
dfs(kk,t);
}
}
int main(){
//freopen("monkey.in","r",stdin);
//freopen("monkey.out","w",stdout);
memset(ans,-1,sizeof(ans));
scanf("%d%d",&n,&m);pre();
for(int i=1;i<=n;i++)
scanf("%d%d",&ch[i][0],&ch[i][1]);
for(int i=1;i<=m;i++){
int p,c;
scanf("%d%d",&p,&c);c--;
if(ch[p][c]==-1)continue;
e[i].a=p;e[i].b=ch[p][c];
ch[p][c]=-1;
}
for(int i=1;i<=n;i++){
if(ch[i][0]!=-1){
if(findf(i)==findf(ch[i][0]))continue;
mergef(i,ch[i][0]);
add(i,ch[i][0]);
add(ch[i][0],i);
}
if(ch[i][1]!=-1){
if(findf(i)==findf(ch[i][1]))continue;
mergef(i,ch[i][1]);
add(i,ch[i][1]);
add(ch[i][1],i);
}
}
for(int i=m;i>=1;i--){
int a=e[i].a,b=e[i].b;
if(a==0&&b==0)continue;
int Fa=findf(a),Fb=findf(b);
int root=findf(1);
if(Fa==Fb)continue;
if(Fa==root)dfs(Fb,i-1);
else if(Fb==root)dfs(Fa,i-1);
mergef(a,b);
add(a,b);
add(b,a);
}
for(int i=1;i<=n;i++)
printf("%d
",ans[i]);
}
/*
3 2
-1 3
3 -1
1 2
1 2
3 1
10 15
5 -1
6 -1
-1 10
9 9
9 10
-1 7
10 -1
5 1
4 3
9 3
6 2
5 2
8 1
5 1
10 1
3 2
4 1
4 2
8 2
1 1
9 1
10 2
7 1
2 1
9 2
*/
山
题目描述
给一座山,如图所示
现在要在山上的某个部位装一盏灯,使得这座山的任何一个部位都能够被看到。给出最小的 y 坐标,如图的+号处 就是 y 坐标最小的安装灯的地方。
输入文件
第一行一个数 N,表示这座山有 N 个点构成,接下来 N 行从左到右给出了这座山的构造情况,每行两个数 Xi,Yi, 表示一个折点,保证 Xi>Xi-1(1<i<=N)。
输出文件
仅输出一行,为最小的 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。
题解
大神口中的半平面交,那么我们直接二分纵坐标,看看是否存在横坐标的取值集合,判断即可
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define LL long long
#define INF (1<<30)
#define eps (1e-8)
using namespace std;
int n;double ans;
struct Point{
double x,y;
Point(double xx=0,double yy=0)
{x=xx;y=yy;}
}p[10000];
struct Line{
Point a,b;
Line(Point aa,Point bb)
{a=aa;b=bb;}
};
bool check(double y){
double L=0,R=1000000;
for(int i=1;i<n;i++){
double k=(p[i].y-p[i+1].y)/(p[i].x-p[i+1].x);
if(abs(k)<eps){
if(y<p[i].y)return false;
}
else {
double x=(y-p[i].y)/k+p[i].x;
if(k>0)R=min(R,x);
else L=max(L,x);
}
if(L>R)return false;
}
return true;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
double L=0,R=1000000;
while(L+0.001<R){
ans=(L+R)/2;
if(check(ans))R=ans;
else L=ans;
}
printf("%.2lf",ans);
}
/*
6
0 0
10 0
11 1
15 1
16 0
25 0
6
0 10
3 7
5 0
6 1
7 4
10 5
*/