训练赛7(小)
导语
涉及的知识点
思维,数学,贪心,随机化
题目
A( HDU 6235)
题目大意:给出一个数n,定义这样一个排列:该排列共有n个数,1~n每个数各出现一次,并且 p i % ∣ p i − p i − 2 ∣ p_i\%\ |p_i-p_{i-2}| pi% ∣pi−pi−2∣为0,求出一个满足条件的序列
思路:使 p i − p i − 2 p_i-p_{i-2} pi−pi−2为1即可
代码
#include <bits/stdc++.h>
using namespace std;
int n,T,data[121212];
int main() {
scanf("%d",&T);
while(T--) {
scanf("%d",&n);
int j=1;
for(int i=1; i<=n; i+=2,j++)
data[i]=j;
for(int i=2; i<=n; i+=2,j++)
data[i]=j;
for(int i=1; i<=n; i++)
printf("%d%c",data[i],i==n?'\n':' ');
}
return 0;
}
B(HDU 6237)
题目大意:给出n堆石子,每堆有 a i a_i ai个,每次操作可以将一个石子在堆之间移动,求最少的操作次数使得存在一个x, a i a_i ai%x=0
思路:首先,x必为总石子数的因数(因为x可以被每一项整除),那么用总石子数的每个质因子来进行尝试即可,对于每个质因数,求出每堆石子需要拿走多少才能被整除,累和需要拿走/缺少的个数,并记录,将这些个数升序排序,因为要求操作数最小,所以采取用小补大的策略,即将拿走/缺少个数小的堆来补对应个数大的堆
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;//必须开long long
ll N,T,a[121212],fac[121212],t[121212];//存石头数,存因子,存余数
int main() {
scanf("%lld",&T);
while(T--) {
scanf("%lld",&N);
ll sum=0,len=0,ans=1e10;//ans必须很大
for(ll i=1; i<=N; i++) {
scanf("%lld",&a[i]);
sum+=a[i];
}
sort(a+1,a+N+1);
for(ll i=2; i<=sum/i; i++)//取质因子
if(sum%i==0) {
while(sum%i==0)
sum/=i;
fac[len++]=i;
}
if(sum>1)//自己就是质因子
fac[len++]=sum;
for(ll i=0; i<len; i++) {
ll x=fac[i],res=0,total=0;//尝试每个质因子
for(ll j=1; j<=N; j++) {
t[j]=a[j]%x;//获取余数
total+=t[j];
}
sort(t+1,t+1+N);
for(ll j=N; j>=1; j--)
if(total>0) {
res+=x-t[j];//补成能整除,用小数补大数
total-=x;
}
ans=min(ans,res);
}
printf("%lld\n",ans);
}
return 0;
}
C(HDU 6242)
题目大意:给出N个点,找到一个圆,至少有一半的点在圆上,保证有解
思路:3点确定一个圆,每次随机选取三个点确定一个圆,再判断圆是否可行,因为至少一半点在圆上,所以大多数点都在一个圆上,因此随机次数较少,N不足5时需要特判
代码
#include <bits/stdc++.h>
using namespace std;
int T,N;
struct point {
double x,y;
} P[212121];
double dis(point a,point b) {
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
point solve(point a,point b,point c) {//三点共圆圆心公式
double x=( (a.x*a.x-b.x*b.x+a.y*a.y-b.y*b.y)*(a.y-c.y)-(a.x*a.x-c.x*c.x+a.y*a.y-c.y*c.y)*(a.y-b.y) )
/ (2*(a.y-c.y)*(a.x-b.x)-2*(a.y-b.y)*(a.x-c.x));
double y=( (a.x*a.x-b.x*b.x+a.y*a.y-b.y*b.y)*(a.x-c.x)-(a.x*a.x-c.x*c.x+a.y*a.y-c.y*c.y)*(a.x-b.x) )
/ (2*(a.y-b.y)*(a.x-c.x)-2*(a.y-c.y)*(a.x-b.x));
return (point) {
x,y
};
}
int main() {
scanf("%d",&T);
while(T--) {
scanf("%d",&N);
for(int i=0; i<N; i++)
scanf("%lf%lf",&P[i].x,&P[i].y);
if(N==1||N==2) {//只有一两个点
printf("%lf %lf %lf\n",P[0].x+1,P[0].y,1.0);
continue;
}
if(N==3||N==4) {//只有三四个点
printf("%lf %lf %lf\n",(P[1].x+P[2].x)/2,(P[1].y+P[2].y)/2,dis(P[1],P[2])/2);
continue;
}
while(1) {
int a=rand()%N;
int b=rand()%N;
int c=rand()%N;//抽三个点出来
int sum=0;
if(a==b||b==c||a==c)
continue;
if(fabs((P[b].y-P[a].y)*(P[c].x-P[b].x)-(P[c].y-P[b].y)*(P[b].x-P[a].x))<=1e-6)
continue;
point q=solve(P[a],P[b],P[c]);//求圆心
for(int i=0; i<N; i++)//判断有多少点在圆上
if(fabs(dis(P[i],q)-dis(P[a],q))<=1e-6)
sum++;
if(2*sum>=N) {
printf("%lf %lf %lf\n",q.x,q.y,dis(P[a],q));
break;
}
}
}
return 0;
}
D(HDU 6243)
题目大意:n条狗,n个笼子,每个狗有编号对应有编号的笼子,现在随机打乱,求有多少只狗不在对应编号的期望
思路:总方案有n!种,对于每条狗,不在对应编号的方案有n!-(n-1)!种,对于每条狗来说概率相等,可以直接拿方案数替代频数,则每条狗的概率为 n ! − ( n − 1 ) ! n ! \frac{n!-(n-1)!}{n!} n!n!−(n−1)!,最后×n得到期望数
代码
#include <bits/stdc++.h>
using namespace std;
int T,N;
int main() {
scanf("%d",&T);
for(int i=1; i<=T; i++) {
scanf("%d",&N);
printf("Case #%d: %.10f\n",i,N-1.0);
}
return 0;
}
E(HDU 6247)
题目大意:有N场比赛,每场比赛需要 a i a_i ai个板子,以防万一,每次比赛要做准备10%的板子(向上取整),询问总共需要多少板子
思路:直接模拟
代码
#include <bits/stdc++.h>
using namespace std;
int T;
int main() {
scanf("%d",&T);
for(int i=1; i<=T; i++) {
printf("Case #%d: ",i);
int N,ans=0;
scanf("%d",&N);
while(N--) {
int t;
scanf("%d",&t);
ans+=(t+9)/10+t;
}
printf("%d\n",ans);
}
return 0;
}
F(HDU 6253)
题目大意:有一个无限大的棋盘,正中心摆了一个马,马走日,给出一个数N,输出N步之后有多少个位置马可能已经走过了
思路:思路较复杂,具体参考文章后的参考文献,注意开unsigned long long
代码
#include <bits/stdc++.h>
using namespace std;
int T,data[5]= {1,9,41,109,205};
int main() {
scanf("%d",&T);
for(int i=1; i<=T; i++) {
unsigned long long N;
scanf("%d",&N);
printf("Case #%d: ",i);
if(N<5)
printf("%d\n",data[N]);
else
cout <<14*N*N-6*N+5<<endl;
}
return 0;
}
G(HDU 6245)
题目大意:两个人打羽毛球,获胜的条件是11:9或者平均的时候比到有一方多两分为止,A赢B需要付出X元,B赢A需要付出Y元,B可以决定输赢,一开始B没有钱且B不能赊账,询问B最多能赢几局
思路:X≤Y,则B需要输一整局攒钱,否则可以输赢交替获胜每一局
代码
#include <bits/stdc++.h>
using namespace std;
int T;
int main() {
scanf("%d",&T);
for(int i=1; i<=T; i++) {
int X,Y,K,ans=0;
scanf("%d%d%d",&X,&Y,&K);
if(X>Y)//如果输了得的钱比赢的输的钱多,则每局都可以赢
ans=K;
else//否则以0:11比分输掉一局来攒钱
ans=11*X*K/(2*X+11*Y);
printf("Case #%d: %d\n",i,ans);
}
return 0;
}