比赛地址
密码:sduacm170716
题目链接
【题意】
行星围绕着恒星转,飞船用最少的时间抵达行星上,不能距离恒星太近,求最少时间。
【分析】
这道题很明显可以二分答案,如果t时间能到行星上那么>t的时间也一定能到行星上。
关键是二分的判断:
飞船在行走的过程中,行星也在公转,我们可以发现,飞船到行星的最短距离不外乎两种情况:
1.直线距离可以到达,不经过内圆r的部分
2.直线距离不能直接到达,需要绕路,而且最短距离是其实位置与内圆的切线+目标位置与内圆的切线+两切点连线的弧长。
如果飞船在t时间内以v速度可以直接飞行最短距离,那么就说明可行。
中间那段弧的圆心角可以通过计算出两点之间的夹角,然后减去两个直角三角形的内角求出。
【Code】
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const double PI = acos(-1.0);
const double EPS = 1e-9;
const int MAX_N = 100000 + 10;
double xp,yp,vp,x,y,v,r,R;
double dist(double x,double y,double x1,double y1)
{
return sqrt((x-x1)*(x-x1)+(y-y1)*(y-y1));
}
bool check(double mid)
{
double p1 = atan2(yp,xp); //初始角度
double p2 = vp * mid / R; //mid时刻角度
double x2 = R * cos(p1 + p2);
double y2 = R * sin(p1 + p2);//mid时刻的位置
double q1 = sqrt(x2 * x2 + y2 * y2 - r * r);
double q2 = sqrt(x * x + y * y - r * r);//分别求起始位置与目标位置的在内圆r上的切线长
double dis = dist(x, y, x2, y2); //求两点间距离
double d;
if (dis// 起始位置到目标位置不经过内圆
d = dis;
}else { // 起始位置到目标位置 需要经过内圆
double d1 = dist(x,y,0,0);
double d2 = dist(x2,y2,0,0);
double angle = acos((d1*d1+d2*d2-dis*dis)/(2*d1*d2))-atan(q1/r)-atan(q2/r);
//求扇形的圆心角
d = angle * r + q1 + q2;
}
if (d <= mid * v + EPS) return true;
return false;
}
int main()
{
scanf("%lf%lf%lf",&xp,&yp,&vp);
scanf("%lf%lf%lf%lf",&x,&y,&v,&r);
R = sqrt(xp*xp + yp*yp);
double l = 0, r = 1e9, mid;
for (int i=1;i<=100;i++){
mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
printf("%.9f\n",l);
return 0;
}
题目链接
【题意】
给出两个字符串,问第一个字符串有多少连续子串使得该子串重新排布后与第二个子串相等,其中第一个子串中的?可以匹配任何字符。
【分析】
可以用前缀和处理一下,sum[i][j] 表示前i位有多少j号字符(0对应?,1对应a …)
sum2[i][j]没必要开二维的数组来着,考试的时候直接写二维了。
然后直接扫一遍判断就好。
【Code】
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const double PI = acos(-1.0);
const int MAX_N = 100000 + 10;
const int N = 26;
char a[MAX_N],b[MAX_N];
int sum1[MAX_N][N+2],sum2[MAX_N][N+2];
int main()
{
scanf("%s",a+1);
scanf("%s",b+1);
int L1 = strlen(a+1), L2 = strlen(b+1);
for (int i=1;i<=L1;i++){
for (int j=1;j<=N;j++)
if (a[i]-'a'+1 == j) sum1[i][j] = sum1[i-1][j] + 1;
else sum1[i][j] = sum1[i-1][j];
if (a[i]=='?') sum1[i][0] = sum1[i-1][0] + 1;
else sum1[i][0] = sum1[i-1][0];
}
for (int i=1;i<=L2;i++){
for (int j=1;j<=N;j++)
if (b[i]-'a'+1 == j) sum2[i][j] = sum2[i-1][j] + 1;
else sum2[i][j] = sum2[i-1][j];
}
int cnt, x, y, ans = 0;
for (int i=1;i+L2-1<=L1;i++){
cnt = 0;
bool flag = false;
for (int j=1;j<=N;j++){
x = sum1[i+L2-1][j]-sum1[i-1][j];
y = sum2[L2][j];
if (x>y){
flag = true;
break;
}
cnt += y-x;
}
if (flag) continue;
if (cnt<=sum1[i+L2-1][0]-sum1[i-1][0]) ans++;
}
printf("%d\n",ans);
return 0;
}
题目链接
【题意】
牛要到河对岸,在与河岸垂直的一条线上,河中有N块石头,给定河岸宽度L,以及每一块石头离牛所在河岸的距离, 现在要去掉M块石头,求去掉M块石头后,剩下的石头之间以及石头与河岸的最小距离的最大值。
【分析】
使最小值最大,最大值最小的问题一般都可以用二分来解决,这个题也是这样。
二分所求的最小距离的最大值mid,记录可以去掉的石头块数cnt,判断cnt<=M是否成立。
【Code】
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const double PI = acos(-1.0);
const int MAX_N = 50000 + 10;
int L, N, M;
int dis[MAX_N];
bool check(int mid)
{
int j = 0,cnt = 0;
for (int i = 1;i <= N + 1;i++)
if (dis[i] - dis[j] >= mid) j = i;
else cnt++;
if (cnt <= M) return true;
return false;
}
int main()
{
int i,left,right,mid;
scanf("%d%d%d",&L,&N,&M);
dis[0] = 0;
dis[N + 1] = L;
for (i = 1; i <= N; i++) scanf("%d",&dis[i]);
sort(dis, dis + 1 + N);
left = 0; right = 2*L;
while (right - left > 1)
{
mid = (right + left) >> 1;
if (check(mid)) left = mid;
else right = mid;
}
printf("%d\n",left);
return 0;
}
题目链接
【题意】
有N个朋友,M组关系,若A认识B,B认识C,那么A认识C。认识的人坐在同一张桌子上,问有多少张桌子。
【分析】
题目描述很容易可以想到用并查集做,认识的人在一个集合,最后看有多少集合就可以。
【Code】
#include
#include
#include
#include
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const double PI = acos(-1.0);
const int MAX_N = 2000 + 10;
int f[MAX_N];
int d[MAX_N];
int findx(int x)
{
if (x==f[x]) return x;
return f[x] = findx(f[x]);
}
void unite(int x,int y)
{
f[findx(x)] = findx(f[y]);
}
int main()
{
int Case;
scanf("%d",&Case);
while (Case--){
int n,m,a,b;
memset(d,0,sizeof(d));
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) f[i] = i;
for (int i=0;iscanf("%d%d",&a,&b);
unite(a,b);
}
for (int i=1;i<=n;i++)
d[findx(i)]++;
int ans = 0;
for (int i=1;i<=n;i++)
if (d[i]!=0) ans++;
printf("%d\n",ans);
}
return 0;
}
题目链接
【题意】
给出一个整数F,代表共有多少组关系,A和B是朋友,B和C是朋友,那么A和C是朋友。然后它们就会构成一个团伙,对于每组关系,输出这个团伙的总数。
【分析】
明显是并查集的题目,重点在于两个方面,一是读入的是人名,需要用map处理一下,二是输出团伙人数,需要在集合合并的时候处理一下。
【Code】
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const double PI = acos(-1.0);
const int MAX_N = 200000 + 10;
map<string,int> st;
int d[MAX_N],f[MAX_N];
int findx(int x)
{
if (x==f[x]) return x;
return f[x] = findx(f[x]);
}
void unite(int x,int y)
{
x = findx(x);
y = findx(y);
if (x==y) {
printf("%d\n",d[x]);
}else {
f[x] = y;
d[y] += d[x];
printf("%d\n",d[y]);
}
}
int main()
{
int Case;
while (~scanf("%d",&Case)){
while (Case--){
st.clear();
for (int i=1;i1;
}
int m, cnt = 0;
string a, b;
scanf("%d",&m);
for (int i=1;i<=m;i++){
cin>>a>>b;
int x,y;
if (st[a]==0) st[a] = ++cnt;
if (st[b]==0) st[b] = ++cnt;
x = st[a];
y = st[b];
unite(x,y);
}
}
}
return 0;
}
题目链接
【题意】
就是类似与蜘蛛纸牌的规则,给出1~10的一个排列,求在i和j位置移动纸牌会有一个abs(i-j)的花费,求合并成一个数的最小花费。
【分析】
我们可以枚举移动i纸牌到j纸牌所在位置(i
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const double PI = acos(-1.0);
const int MAX_N = 200000 + 10;
const int N = 10;
int n,t,ans,a[N+2];
bool v[N+2];
void dfs(int step, int sum)
{
if (sum>=ans) return;
if (step>=9){
ans = sum;
return;
}
for (int i=1;i<=N;i++)
if (!v[i]){
v[i] = true;
for (int j=i+1;j<=N;j++)
if (!v[j]){
dfs(step+1,sum+abs(a[i]-a[j]));
break;
}
v[i] = false;
}
}
int main()
{
int Case ;
scanf("%d",&Case);
while (Case--){
int x;
for (int i=1;i<=10;i++) {
scanf("%d",&x);
a[x] = i;
}
ans = INF;
dfs(0,0);
printf("%d\n",ans);
}
return 0;
}
题目链接
【题意】
有一些长度相等的棍子,被随机的砍成了若干棍子,求原来的棍子长度的最小值。
【分析】
明显设所有棍子总长度sum,原来每根棍子长度为i,那么sum % i == 0.我们可以排序后从大到小枚举,然后加上几个剪枝。
【Code】
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const double PI = acos(-1.0);
const int MAX_N = 100 + 10;
int sum, n;
int a[MAX_N];
bool v[MAX_N];
bool cmp(const int &a,const int &b)
{
return a > b;
}
bool dfs(int cnt, int len, int now, int t)
{
if (cnt == sum / t) return true;
if (len == t) return dfs(cnt+1,0,0,t);//特判
for (int i = now; i < n; i++){
if (!v[i]&&len+a[i]<=t){
v[i] = true;
if (dfs(cnt,len+a[i],i+1,t)) return true;
v[i] = false;
//剪枝:如果第一根始终要被废弃,说明这种方案一定不行,这里的剪枝会节省很多的无用搜索。
while (i+1 < n && a[i+1] ==a[i]) i++;
//剪枝:如果当前和上一次搜到的木棒是一样长的则没必要再搜一次了。
}
}
return false;
}
int main()
{
while (~scanf("%d",&n)&&n){
sum = 0;
for (int i=0;iscanf("%d",&a[i]);
sum += a[i];
}
sort(a,a+n,cmp);
int ans = 0;
for (int i=1;i<=sum;i++) if (sum % i == 0){
memset(v,0,sizeof(v));
if (dfs(0,0,0,i)){
ans = i;
break;
}
}
printf("%d\n",ans);
}
}
题目链接
【题意】
给定长度为n的一个序列,求该序列的子序列,该子序列不减且在原序列中标号递增。输出前p个序列,若不足p个输出全部。输出时按长度输出,长度相同则按字典序输出,且需要保证子序列不重复输出。
【分析】
直接在原序列中枚举复杂度太高,我们可以排序后枚举,注意需要保证在原序列中的位置是递增的所以需要记录一下原序列中的位置。
然后其实这里面最重要的是下面的两个剪枝。
1:按位数搜索的话,显然如果不存在长度为i的序列,也一定不存在长度为i+1的序列,后面的可以全部剪掉。
2:然后就是代码中flag和pre的部分来处理数列中连续相等的部分。
开始设置一个flag为false,第一次访问该元素的时候flag为true,然后用一个pre保留当前位置的数的值,然后后面在搜相同长度的序列时,如果当前的数与pre是一样的,说明先前已经搜过了,直接continue就行了,否则,pre就保留这个数,然后搜当前长度+1的数。。。
【Code】
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const double PI = acos(-1.0);
const double EPS = 1e-9;
const int MAX_N = 1000 + 10;
const int MAX_M = 2000000 + 10;
int a[MAX_N], b[MAX_N];
int n, m, cnt;
struct node{
int x,num;
} p[MAX_N];
bool ok;
bool cmp(const node &a,const node &b)
{
if (a.x == b.x) return a.num < b.num;
return a.x < b.x;
}
void dfs(int step,int now,int tot)
{
if (cnt>=m) return;
if (step == tot){
cnt++;
ok = true;
for (int i=1;iprintf("%d ",a[i]);
printf("%d\n",a[tot]);
return;
}
int pre;
bool flag = false;
for (int i=now+1;i<=n;i++)
if (p[i].num>p[now].num){
if (!flag){
flag = true;
pre = p[i].x;
}else if (pre == p[i].x) continue;
pre = p[i].x;
a[step+1] = p[i].x;
dfs(step+1,i,tot);
}
}
int main()
{
while(~scanf("%d%d",&n,&m)){
for (int i=1;i<=n;i++){
scanf("%d",&p[i].x);
p[i].num = i;
}
sort(p+1,p+n+1,cmp);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
cnt = 0;
for (int i=1;i<=n;i++){
ok = false;
dfs(0,0,i);
if (cnt >= m || !ok) break;
}
puts("");
}
}