A: NEFU 1295 机器人
思路:用结构体将输入数据存起来,将数据进行反转存入从小到大排的优先队列,输出时按原先数字输出。
注意:输出格式为元素之间有一个空格,末尾没有空格。
#include <bits/stdc++.h>
using namespace std;
struct sa{
int zs,fs;//分别存入原先数据和反转后的数据
};
int n,x,y;
bool operator < (const sa &a,const sa &b)
{
return a.fs>b.fs;
}
int fan(int k)//将数据进行反转
{
int gw=0,xs=0;
while(k)
{
gw=k%10;
xs=xs*10+gw;
k/=10;
}
return xs;
}
int main()
{
priority_queue <sa> s;
while(cin>>n)
{
for(int i=1;i<=n;i++)
{cin>>x;
y=fan(x);
s.push({x,y});
}
while(!s.empty())
{
s.size()==1?printf("%d\n",s.top()):printf("%d ",s.top());
s.pop();
}
}
return 0;
}
B:NEFU 1311 纸牌游戏
思路:循环队列。
注意:这题同样要注意输出格式。
#include <bits/stdc++.h>
using namespace std;
int t,n;
queue <int> s;
int main()
{
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++)
{
s.push(i);
}
while(s.size()>1)//当队列中只剩1个元素时跳出
{
int tmp=s.front();
s.pop();
s.size()==1? printf("%d\n",tmp):printf("%d,",tmp);
int u=s.front();
s.pop();
s.push(u);//将扔掉后的队列中第一张牌放在最后
}
printf("%d\n",s.front());
while(!s.empty()){s.pop();}
}
return 0;
}
C:NEFU 2114 咸鱼连突刺
思路:用素数筛先将1e6范围内素数打表,然后找范围内的最大最小素数即可。
注意:
1.输出今天的伤害总值,记得用 long long 进行存储。
2.查找的是闭区间内的素数。
3.若范围内只有一个素数,则该数*2。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=1e6+1;
int n,x,cut,prime[N],h;
bool b[N];
void getprime ()//素数筛打表
{
memset(b,1,sizeof(b));
b[0]=b[1]=0;
for(int i=2;i<=N;i++)
{
if(b[i]) prime[++cut]=i;
for(int j=1;j<=cut&&prime[j]*i<=N;j++)
{
b[prime[j]*i]=0;
if(i%prime[j]==0) break;
}
}
}
int main()
{
int t,l,r;
getprime();
cin>>t;
long long ans=0;//记录伤害总值
while(t--)
{
cin>>l>>r;
int minn=0,maxx=0;
for(int i=l;i<=r;i++)//求最小素数
{
if(b[i])
{minn=i;break;}
}
for(int i=r;i>=l;i--)//求最大素数
{
if(b[i])
{maxx=i;break;}
}
ans+=minn+maxx;
}
printf("%lld\n",ans);
return 0;
}
D:NEFU 2110 库特的合并果子
思路:简单的合并果子,贪心思想+优先队列。
注意:注意输出的是每一轮消耗的体力值。
#include <bits/stdc++.h>
int n,x,cut;
using namespace std;
priority_queue<long long,vector<long long>,greater<long long> > s;
int main()
{
while(cin>>n)
{for(int i=1;i<=n;i++){
cin>>x;
s.push(x);
}
cut=0;
if(s.size()==1)printf("0\n");//如果只有一堆则输出0,不过这题貌似不需要WAW
while(s.size()>1){
long long t=s.top();
s.pop();
long long p=s.top();
s.pop();
long long l=t+p;
s.push(l);
s.size()==1?printf("%lld\n",l):printf("%lld ",l);
}
while(!s.empty()){
s.pop();
}
}
return 0;
}
补充:用优先队列的解法时间复杂度为O(nlogn),洛谷大犇还有为O(n)的更强解法,解法指路。
E:NEFU 2111 库特的素数队列(1)
思路:
素数筛打表。
1.用循环队列实现。
2.数组也可以,不过从个人感觉貌似效率没有优先队列高。
注意:注意考虑队列中只有一个孩子的情况。
队列实现:
#include <bits/stdc++.h>
using namespace std;
queue<int> s;
const int N=1e7+1;
int n,x,cut,prime[N],h;
bool b[N];
void getprime ()//打表
{
memset(b,1,sizeof(b));
b[0]=b[1]=0;
for(int i=2;i<=N;i++)
{
if(b[i]) prime[++cut]=i;
for(int j=1;j<=cut&&prime[j]*i<=N;j++)
{
b[prime[j]*i]=0;
if(i%prime[j]==0) break;
}
}
}
int main()
{
getprime ();
std::ios::sync_with_stdio(0);
cin>>n;
while(n--)
{cin>>x;
for(int i=1;i<=x;i++){
s.push(i);
}
while(s.size()>1)
{
for(int i=1;i<=x;i++)
{
if(i==1){h=s.back();}//记录本次循环末尾元素
int tmp=s.front();
s.pop();
if(b[i]) {s.push(tmp);}//判断所报是否为素数
if(h==tmp&&s.size()!=1){i=0;x=s.size();}//若本次循环末尾元素与队首元素相等,进入下次循环
}
}
while(!s.empty())
{
printf("%d\n",s.front());
s.pop();
}
}
return 0;
}
数组实现:
用一个数组报数,用一个数组存剩下的小朋友。
#include <bits/stdc++.h>
using namespace std;
const int N=1e7+1;
int n,x,cut,prime[N],h,p;
bool b[N];
int a[N],c[N];
void getprime ()
{
memset(b,1,sizeof(b));
b[0]=b[1]=0;
for(int i=2; i<=N; i++)
{
if(b[i])
prime[++cut]=i;
for(int j=1; j<=cut&&prime[j]*i<=N; j++)
{
b[prime[j]*i]=0;
if(i%prime[j]==0)
break;
}
}
}
int main()
{
getprime ();
std::ios::sync_with_stdio(0);
cin>>n;
while(n--)
{
cin>>x;
memset(a,0,sizeof(a));
memset(c,0,sizeof(c));//因为是多组循环所以要注意初始化数组
p=0;
for(int i=1; i<=x; i++)//存所有小朋友
{
a[++p]=i;
}
while(p>1)
{
int j=0;
for(int i=1; i<=p; i++)
{
if(b[i])
c[++j]=a[i];//将报素数的小朋友存入c数组
}
memset(a,0,sizeof(a));
for(int i=1; i<=j; i++)
a[i]=c[i];//用a数组存剩下的小朋友
memset(c,0,sizeof(c));
p=j;
}
printf("%d\n",a[1]);
}
return 0;
}
F:NEFU 2112 纸牌游戏
大佬思路:
在线
- 首先我们可以得出一个结论,显然在轮数相同情况下最后剩下的人的序号是确定的。随着n增加,只有让轮数增加才能使最后答案变化,那什么时候轮数会增加呢?我们不难发现只有当多了一个人并且他站到了之前的最后一轮,使最后一轮的人数从一个变成两个才能增加轮数,那怎么确定每增加一个人他是否能站到最后一轮呢?我们递推记录一下每个点的答案,如果n的因子数对应的答案和n-1对应的答案相同了,可以看出n变到n的因子需要一轮,所以可以看出此时n比n-1多一轮了,而n又是在最后一轮两个数的时候处于第二个数的位置,所以n点对应输出的答案就是n。
- 我们通过上面(1)这个版本也很容易看出最后可能成为输出答案的数并不多,那么我们每次二分找每次答案的第一次变化时的值迭代递推过去就行,但是这种方法其实是带2个log的(一个是二分的log,一个是迭代次数的log,两个log相乘关系),但是对于t=3e4也可以轻松过的。
离线
既然我们知道答案不多而且每个数x对应的答案都是答案组里小于等于x的最大值,我们只要找到答案组就ok了。提供三种找答案组的方法。
- 手动找 我们输入上限1e7可以得到一个答案x,然后我们输入x-1得到答案y,重复11次就可以找到所有答案组。2. 二分 手动二分或者用程序二分找答案第一次变化的位置即可,也是反复找11次即可
- 二分 手动二分或者用程序二分找答案第一次变化的位置即可,也是反复找11次即可
- 找规律,也可以把前100个数打个表,我们就可以发现答案组有如下规律: 答案组第一个成员是1,然后下一个成员是第一个素数2,然后下一个成员是第2个素数3,然后下一个成员是第3个素数5,然后下一个成员是第5个素数11,然后下一个成员是第11个素数31…找到了这个规律直接把答案组直接递归输出出来即可。
菜鸡(即本人)思路:
打表找规律,你会发现当数据越来越大时,答案数几乎不变了,再看看与之相对应的输出数据,哇!你会发现某个范围内都是一个答案耶,而且刚好是从这个数出现开始直到下个数之间的范围耶,剩下的事情就是找范围啦。
注意:范围的确定。
附上一小部分打表数据:
1=1,
2=2,
3=3,
4=3,
5=5,
6=5,
7=5,
8=5,
9=5,
10=5,
11=11,
12=11,
13=11,
14=11,
15=11,
16=11,
17=11,
18=11,
19=11,
20=11,
21=11,
22=11,
23=11,
24=11,
25=11,
26=11,
27=11,
28=11,
29=11,
30=11,
31=31,
32=31,
33=31,
34=31,
35=31,
36=31,
37=31,
38=31,
39=31,
40=31,
41=31,
42=31,
43=31,
44=31,
45=31,
46=31,
47=31,
48=31,
49=31,
50=31,
51=31,
52=31,
53=31,
54=31,
55=31,
56=31,
57=31,
58=31,
59=31,
60=31,
61=31,
62=31,
63=31,
64=31,
65=31,
66=31,
67=31,
68=31,
69=31,
70=31,
71=31,
72=31,
73=31,
74=31,
75=31,
76=31,
77=31,
78=31,
79=31,
80=31,
81=31,
82=31,
83=31,
84=31,
85=31,
86=31,
87=31,
88=31,
89=31,
90=31,
91=31,
92=31,
93=31,
94=31,
95=31,
96=31,
97=31,
98=31,
99=31,
100=31,
101=31,
102=31,
103=31,
104=31,
105=31,
106=31,
107=31,
108=31,
109=31,
110=31,
111=31,
112=31,
113=31,
114=31,
115=31,
116=31,
117=31,
118=31,
119=31,
120=31,
121=31,
122=31,
123=31,
124=31,
125=31,
126=31,
127=127,
128=127,
129=127,
130=127,
131=127,
132=127,
133=127,
134=127,
因数据太多就只列这一部分啦,如果有兴趣可以自己打打表玩玩啦。
简洁写法:
看这里看这里
在e上面改进的写法:
(本人当时提交的代码,其实当n比较小的时候,变化比较快,列范围有些麻烦,所以本人只写了5381之后的情况,也是能ac的)
#include <bits/stdc++.h>
using namespace std;
queue<int> s;
const int N=1e7+1;
int n,x,cut,prime[N],h;
bool b[N];
void getprime ()
{
memset(b,1,sizeof(b));
b[0]=b[1]=0;
for(int i=2;i<=N;i++)
{
if(b[i]) prime[++cut]=i;
for(int j=1;j<=cut&&prime[j]*i<=N;j++)
{
b[prime[j]*i]=0;
if(i%prime[j]==0) break;
}
}
}
int main()
{
getprime ();
std::ios::sync_with_stdio(0);
cin>>n;
while(n--)
{cin>>x;
//printf("%d=",x);
if(x==1) {printf("1\n");}
else if(x>=5381&&x<52711){printf("5381\n");}
else if(x>=52711&&x<648391){printf("52711\n");}
else if(x>=648391&&x<9737333){printf("648391\n");}
else if(x>9737333){printf("9737333\n");}
else{
for(int i=1;i<=x;i++){
if(b[i])s.push(i);
}
x=s.size();
while(s.size()>1)
{
for(int i=1;i<=x;i++)
{
if(i==1){h=s.back();
//printf("%d %d\n",h,x);
}
int tmp=s.front();
s.pop();
if(b[i]) {s.push(tmp);
//printf("%d %d\n",i,tmp);
}
if(h==tmp&&s.size()!=1){i=0;x=s.size();}
}
}
while(!s.empty())
{
printf("%d\n",s.front());
s.pop();
}
}
}
return 0;
}
G:NEFU 2023 库特的绳子
思路:贪心的思想+优先队列
这题算是序列合并-优先队列这题的变形,当然此题也是洛谷上的一道经典题洛谷 P1631 序列合并。
用两个从小到大排列的优先队列分别存储绳子长度和钉子间隔,对于钉子间隔,先把每个紧邻的钉子的间隔以及钉子位置用存入队列,如果这个间隔可以挂上绳子,则这个区间被利用,右钉子向右移动一个位置获取信息存入数组,重复操作直到所有绳子都被挂上。
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,m,a[N],x;
struct sa {
int jg;//间隔
int ldz;//左钉子的位置
int rdz;//右钉子的位置
};
bool operator < (const sa &a,const sa &b)
{
return a.jg>b.jg;
}
priority_queue <int,vector<int>,greater<int> > sz;
priority_queue <sa> dz;
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);//接收钉子位置
}
for(int i=1;i<=m;i++)
{
scanf("%d",&x);
sz.push(x);//接收绳子长度
}
for(int i=1;i<n;i++)
{
dz.push({a[i+1]-a[i],i,i+1});//将钉子间隔压入队列
}
int f=1;
while(!sz.empty())
{
int tmp=sz.top();
sz.pop();
if(dz.empty()||dz.top().jg>tmp)//若没有间隔或绳子长度小于间隔,不符合,跳出
{
f=0;break;
}
else//默认绳子长度大于间隔
{
sa tm=dz.top();
dz.pop();
if(tm.rdz+1<=n)//左钉子不为最后一个钉子
{
dz.push({a[tm.rdz+1]-a[tm.ldz],tm.ldz,tm.rdz+1});//右钉子向右移动一位,获取其长度;
}
}
}
f==1?printf("yes\n"):printf("no\n");
}
return 0;
}