NEUQ-acm第一次双周赛补题

7-1 Lily

题目描述

百合花(Lily)是一种美丽的花。她通常一年只开一次花,所以如果你看到百合花盛开,它会非常珍贵。然而,她对猫有剧毒,所以你必须注意让好奇的猫远离可爱的百合花。

你有n个网格的土壤土地排成一行,从1到n,其中一些是百合花。我们不想伤害百合,也不想伤害猫。你可以在网格上放一些猫粮,但对于任何有猫粮的网格i,在区域[i−1,i+1]不得含有百合花。你喜欢猫和百合,所以你想最大限度地增加有猫粮的格子。

设计满足上述要求的计划。

输入格式

有一个整数n(1≤n≤1000)表示网格的数量。

第二行包含仅由“L”和“.”组成的字符串R,表示有和没有百合花的格子。

输出格式

输出包含一行,字符串R′仅由“L”、“”组成和“C”,其中“C”表示在满足上述要求的同时分配给R中空网格的猫粮。

输入样例

5
..L..

输出样例

C.L.C

思路

遍历每一格子,当左右都没有花,则放猫粮
第一格和最后一格单独考虑
另:注意点放到代码注释中喽

代码

#include <bits/stdc++.h>
using namespace std;
int main(){
    int n;
    cin >> n;
    char r[n];
    for(int i = 0; i < n; i++)  cin >> r[i];
    if(r[0]=='.' && r[1]!='L')  r[0]='C';
    if(r[n-1]=='.' && r[n-2]!='L')  r[n-1]='C';
    //错误一:如果n=3,r[n-2]=C ,下面这种写法就错了
    //if(r[n-1]=='.' && r[n-2]=='.')  r[n-1]='C';
    for(int i = 1; i < n-1; i++){
        if(r[i]=='.' && r[i-1]!='L' && r[i+1]!='L')
            r[i]='C';
    }
    for(int i = 0; i < n; i++)  cout << r[i] ;
}

7-2 a * b

题目描述

给出两个不超过1000位的十六进制数a,b。
求a∗b的值

输入格式

输入共两行,两个十六进制的数

输出格式

输出一行,表示a∗b

输入样例

1BF52
1D4B42

输出样例

332FCA5924

思路

高精度问题,存储时将16进制转换为10进制存储,及输出时再将10进制转换回16进制,其他与Week1中高精度乘法一致

代码

#include <bits/stdc++.h>
using namespace std;
const int max1=1e3+5;
const int max2=1e7+5;
int a[max1],b[max1],c[max2];
string s1,s2;
int CharzhuanDig(char x){
    if(x >='0' && x<='9')  return x-'0';
    else  return x-55;
}
void get(){
    int lena=s1.length();
    int lenb=s2.length();
    a[0]=lena;
    for(int i = 1; i<= lena; i++){
        a[i]=CharzhuanDig(s1[lena-i]);
    }
    b[0]=lenb;
    for(int i = 1; i<= lenb; i++){
        b[i]=CharzhuanDig(s2[lenb-i]);
    }
}
void multi(){
    for(int i=1;i<=a[0];++i){
		for(int j=1;j<=b[0];++j){
			c[i+j-1]+=a[i]*b[j];
		}
	}
    c[0]=a[0]+b[0];
	for(int i=1;i<c[0];++i){
		if(c[i]>=16){
			c[i+1]+=c[i]/16;
			c[i]%=16;
		}
	} 
	while(c[c[0]]==0&&c[0]>0){
		c[0]--;
	} 
}
void print(){
    for(int i = c[0]; i >= 1; i--){
        if(c[i]<10)  cout << c[i];
        else{
            cout << static_cast<char>(c[i]+55);
        }
    }
}
int main(){
    cin >> s1;
    cin >> s2;
    get();
    multi();
    print();
}

7-3 山头狙击战

题目描述

小明为了掩护大部队,单枪匹马同敌人周旋,后来被敌人包围在某山头……等等,为什么怎么听怎么像狼牙山五壮士!不过不用着急,这次小明携带了足够的弹药,完全可以将涌上来的敌人一个一个干掉。小明是个神枪手,只要他的枪膛中有子弹,他就能将在他射程m(用从敌人位置到山头的直线距离算)以内的一个敌人瞬间射杀。但如果在射程内没有敌人,出于节约子弹考虑和面子问题,小明会等待敌人靠近然后射击。
正当小明为自己的强大而自我膨胀时,他忽然发现了一个致命的失误:他携带的枪是单发枪,每射出一发子弹都必须花k秒钟的时间装子弹。而凶残的敌人才不会花时间等你换子弹呢。他们始终在以1m/s的速度接近山头。而如果在一个敌人到达山头时小明无法将他击毙,那么我们可怜的小明就将牺牲在敌人的刺刀下。现在小明用心灵感应向你发出求助:要保住自己的性命并且歼灭所有敌人,小明最多只能用多少时间给枪装上一发子弹?
说明:假设一开始小明的枪中就有一发子弹,并且一旦确定一个装弹时间,小明始终会用这个时间完成子弹的装卸。希望你能帮助小明脱离险境。

输入格式

每组输入数据,第一行有两个整数n和m,(2≤n≤100,000; 1≤m≤10,000,000)n代表敌人个数,m代表小明的射程。
接下来有n行,每行一个整数mi,(1≤mi≤10,000,000),代表每个敌人一开始相对山头的距离(单位为米)。

输出格式

每组输出数据仅有一个整数,代表小明的换弹时间(单位为秒)。

样例输入

6 100
236
120
120
120
120
120

样例输出

25

思路

二分答案,显然直接求答案需要满足的条件太多,但是验证一个答案正不正确却比较容易。
那么,怎么验证:
判断敌人距你的位置,如果在射程范围内,说明这个敌人可以解决,如果和你距离小于0了,说明这个答案不行,继续二分
那么怎么求距离:
只需要求出准备射击这个敌人前耗费的总时间,然后用初始距离减去时间*速度即距离

代码

#include <bits/stdc++.h>
using namespace std;
const int Max=1e7+5;
int n,m;
int k;
int dis[Max];
bool check(int k){
    int t=dis[0]+k;
    for(int i = 2; i <= n; i++){
        if(dis[i]-t>m) t+=dis[i]-t-m;
        if(dis[i]-t<0){
            return false;
        }
        t+=k;
    }
    return true;
}

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> dis[i];
    }
    sort(dis,dis+n+1);
    if(dis[1]>m) dis[0]=dis[1]-m;
    //for(int i = 0; i<= n; i++)  cout << dis[i];
    int l=1,r=m;
    int ans;
    while(l<=r){
        k=(l+r)/2;
        if(!check(k)) r=k-1;
        else  ans=k,l=k+1;
    }
    cout << ans << endl;
}

注意:
(1)第一发子弹是装好的
(2)不在射程范围内时,要等走过来才射击,要增加 t t t
(3)那么基于问题(2),考虑到敌人是一起走的,先要对存储的数组从小到大排序,否则增加 t t t 时走在前面的敌人可能已经到了身边。

7-4 Reversing Linked List

题目描述

Given a constant K and a singly linked list L, you are supposed to reverse the links of every K elements on L. For example, given L being 1→2→3→4→5→6, if K=3, then you must output 3→2→1→6→5→4; if K=4, you must output 4→3→2→1→5→6.

Input Specification

Each input file contains one test case. For each case, the first line contains the address of the first node, a positive N (≤10
5
) which is the total number of nodes, and a positive K (≤N) which is the length of the sublist to be reversed. The address of a node is a 5-digit nonnegative integer, and NULL is represented by -1.

Then N lines follow, each describes a node in the format:

Address Data Next

where Address is the position of the node, Data is an integer, and Next is the position of the next node.

Output Specification

For each case, output the resulting ordered linked list. Each node occupies a line, and is printed in the same format as in the input.

Sample Input

00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218

Sample Output

00000 4 33218
33218 3 12309
12309 2 00100
00100 1 99999
99999 5 68237
68237 6 -1

思路

主要是看到大片的英文就不敢做了,其实只不过是普通的链表问题
注意:题中有孤点情况,需要统计一下真是链接到一起的有几个点

代码

#include <bits/stdc++.h>
using namespace std;
const int Max=1e5+5;
int first;
int n;
int k;
int num;  //记录真实的n
struct node{
    int data;
    int next;
}s[Max];
void print(){
    for(int i = 0; i < num; i++){
        printf("%05d ",first);
        printf("%d ",s[first].data);
        if(i<num-1)
        printf("%05d\n",s[first].next);
        else  cout << s[first].next << endl;
        first=s[first].next;
    }
}
void Reverse(int local, int k){
    int ind=local;
    //cout << ind << endl;
    for(int j = 0; j<k-1; j++){
        ind=s[ind].next;
    }
   // cout << ind << endl;
    for(int i = 0; i < k-1; i++){
        int localNext=s[local].next;
        s[local].next=s[ind].next;
        s[ind].next=local;
        local=localNext;
    }
    //cout << local << endl;
}
int main(){
    cin >> first;
    cin >> n >> k;
    for(int i = 0; i < n; i++){
        int address;
        cin >> address;
        cin >> s[address].data >> s[address].next;
    }
    int x=first;
    while(x!=-1){
        num++;  
        x=s[x].next;
    }
    int now=first;
    for(int i = 0; i < k-1; i++){
        first=s[first].next;
    }
    for(int i=0; i< num/k; i++){
        Reverse(now,k);
        //cout << now << " ";
        int now1=s[now].next;
        int now2=now;
        if((1+i)<num/k && k!=1){
            int x=0;
            for(int j=0; j < k; j++){
                x=s[now].next;
                now=s[now].next;
            }
            s[now2].next=x;
        }
        now=now1;
        //cout << now << " ";
        //cout << s[now].next << endl;
    }
    print();
}

7-5 一元三次方程

题目描述

给定一个形如 a x 3 + b x 2 + c x + d = 0 ax^3+bx^2+cx+d=0 ax3+bx2+cx+d=0 的一元三次方程。
已知该方程有三个不同的实数根(根与根之差的绝对值≥ 1 0 − 6 10^{−6} 106),且根范围均在[p,q]之间,你需要解出这个方程的三个根。

输入格式

第一行一个整数T(1≤T≤1000),表示有T组数据

接下来T行,每行6个实数,分别表示a,b,c,d,p,q

数据保证: − 1 0 2 ≤ p , q ≤ 1 0 2 −10^{2}≤p, q≤10^{2} 102p,q102
且对于 ∀ x ∈ [ p , q ] , − 1 0 6 ≤ f ( x ) ≤ 1 0 6 ∀x∈[p,q], −10^6≤f(x)≤10^6 x[p,q],106f(x)106

输出格式

输出三个实数,表示方程的三个解。
你的答案可以以任意顺序输出。
一个答案被认为是正确的,当且仅当其与标准答案的绝对误差不超过10−6

输入样例

在这里给出一组输入。例如:

1
1.000000 -5.000000 -4.000000 20.000000 -10.000000 10.000000

输出样例

在这里给出相应的输出。例如:

-2.000000 2.000000 5.000000

思路

由于三次函数不是单调的,无法直接二分,但是根据题意,一定存在两极点点 x x 2 , x x 1 xx_2, xx_1 xx2,xx1将区间分为 [ p , x x 2 ] , [ x x 2 , x x 1 ] , [ x x 1 , q ] [p,xx_2],[xx_2,xx_1],[xx_1,q] [p,xx2],[xx2,xx1],[xx1,q]。函数在每个区间内单调,包含仅且包含1个解。
而一元三次方程韦达定理高等代数中学过吧,所以继续用一次二分求得一个解,另外两个套公式就解的啦。
韦 达 定 理 { x 1 + x 2 + x 3 = − b a x 1 x 2 x 3 = − d a 韦达定理 \begin{cases} x_1+x_2+x_3={-b\over a} \\ x_1x_2x_3={-d\over a}\\ \end{cases} {x1+x2+x3=abx1x2x3=ad

(1)求出两极点 x x 1 , x x 2 xx_1, xx_2 xx1,xx2
求导 3 a x 2 + 2 b x + c = 0 3ax^2+2bx+c=0 3ax2+2bx+c=0,利用求根公式
(2)利用二分求其中一个解 x 2 x_2 x2
(3)韦达定理代公式求 x 1 , x 3 x_1,x_3 x1,x3

代码

#include <bits/stdc++.h>
using namespace std;
const double m=1e-7;
const double n=1e-6;
int T;
double a,b,c,d,p,q;  //参数和区间
double x1,x2,x3;  //三个解
double xx1,xx2;  //两极点
double f(double x){
   double y = a*x*x*x + b*x*x + x*c +d;
   return y;
}
double ff(double x){
   double y= 3*a*x*x + 2*b*x + c;
   return y;
}
bool tonghao(double y1, double y2){
   if(y1>=0 && y2<=0)  return false;
   else if(y1<=0 && y2>=0)  return false;
   else  return true;
}

double jie(double p, double q){
   //double ans;
   double mid;
   while(q-p>m){
       mid=(p+q)/2.0;
       if(tonghao(f(p),f(mid))) p=mid;
       else q=mid;
       if(tonghao(f(mid),f(q))) q=mid;
       else p=mid;
   }
   return p;
}
int main(){
   cin >> T;
   for(int i = 0; i < T; i++){
       cin >> a >> b >> c >> d >> p >> q;
   
   xx1=(-2*b+sqrt(4*b*b-12*a*c))/(6*a);
   xx2=(-2*b-sqrt(4*b*b-12*a*c))/(6*a);
   //cout << p << " " << xx1 << " " << xx2 << " " << q << endl;
   //cout << f(p) << " " << f(xx1) << " " << f(xx2) << " " << f(q) << endl;
   x2=jie(xx2,xx1);
   //x1=jie(p,xx2);
   //x3=jie(xx1,q);
   //cout <<  f(q) << endl;
   cout << f(p) << " " << f(xx1) << " " << f(xx2) << " " << f(q);
   x1=(-b/a-x2-sqrt((-b/a-x2)*(-b/a-x2)-4.0*(-d/a/x2)))/2.0;
   x3=-b/a-x1-x2;
   /* double e = b * b - 3 * a * c;
    double f = b * c - 9 * a * d;
    double g = (2 * e * b - 3 * a * f) / (2 * sqrt(e * e * e));
    double h = acos(g);
    x1 = (-b - 2 * sqrt(e) * cos(h / 3))/(3 * a);
    x3 = (-b + sqrt(e) * (cos(h / 3)+sqrt(3) * sin(h/3))) / (3 * a);
    x2 = (-b + sqrt(e) * (cos(h/3) - sqrt(3) * sin(h/3))) / (3 * a);
   */
   //cout << fixed << setprecision(6) << x1 << " " << x2 << " " << x3 << endl;
   printf("%.6f %.6f %.6f",x1,x2,x3);
   }
}

看到注释就知道调试过程有多痛苦了我甚至用了盛金公式直接求解,但最最最难受的是debug了好久,一直以为自己解法或者精度的问题,最后发现是把求解过程放到循环外边去了,巧的是每次测试样例T我都输的1… …整emo了

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是夕颜呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值