admin管理员组文章数量:1621899
虽然这回没参加这次比赛,但是赛后看题时发现有许多的题都很有意思,就自己写了一下,并且写了一个题解,希望对大家有所帮助!!!。
A题 数字统计
防止答案过大,用long long,我看到范围只到10000,所以想直接暴力l到r中所有出现的2。
#include<bits/stdc++.h>
using namespace std;
long long check(int x) //获得x中的2的数量
{
long long res=0;
while(x)
{
if(x%10==2) res++;
x=x/10;
}
return res; //返回res
}
int main()
{
int l,r;
cin>>l>>r;
long long ans=0;
for(int i=l;i<=r;i++)
{
ans=ans+check(i);
}
printf("%lld",ans);
}
B题 凌波微步
hhh 开始我还以为是dp,然后看到范围是1000000000(dp必超时),所以重新看了题发现是可以随便向前向后走,所以因为规则上只能从小到大跳,所以只需要把重复的数去掉,剩下的数的数量就是本题答案,这时咱们学到的stl中的set就有了作用,set会自动将添加进去的重复的数忽略,所以只需要把所有数放进set中再得到set中存的数的数量,这道题就能解出来了。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
set<int> a;
int main()
{
int t;
cin>>t;
while(t--) //多组输入
{
int n;
cin>>n;
a.clear(); //不要忘了清空a中的数
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
a.insert(x);
}
cout<<a.size()<<endl;
}
}
C题 老子的全排列呢
hhh你可以试试直接自己写出全排列,当然正常情况是自己写一个dfs。
而知道了使用dfs算法就要去考虑如何写出来。在这里递归需要求解的问题是“把指定的n个整数按照任意次序排序”,在每次递归中,我们尝试把每个可用的数作为数列中的下一个数,求解“把剩余的n-1个数按照任意次序排列”这个规模更小的子问题,不多说上代码!
#include<bits/stdc++.h>
using namespace std;
const int N=10;
int n;
int path[N]; //路径
bool st[N]; //检查是否此数已经被使用
void dfs(int u) //已经添加了u个数
{
if(u==n) //当已经添加n个数,输出已经得到的路径
{
for(int i=0;i<n;i++) printf("%d ",path[i]);
cout<<endl;
return;
}
for(int i=1;i<=n;i++)
{
if(!st[i]) //没有被使用的数
{
path[u]=i; //放进路径中
st[i]=true; //此数已经被使用
dfs(u+1); //递归到下一层
st[i]=false; //递归后恢复现场
}
}
}
int main()
{
n=8;
dfs(0); //0代表已经添加了0个数
return 0;
}
D题 爱因斯坦的名言
签到题,有个小坑点,要输出“。
#include<bits/stdc++.h>
using namespace std;
int main()
{
cout<<"\"Genius is 1% inspiration and 99% perspiration.\"";
}
E题 武藏牌牛奶促销
首先感谢木哥哥的帮助,什么时候会走进循环呢,当每次交换后的a和b个瓶盖都成稳定或者增长情况,这个就成为了稳定情况。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int a,b,c,d;
while(cin>>a>>b>>c>>d) //多组输入
{
ll ans=0;
int xh=0;
int e=c,f=d;
bool flag=true;
while(c>=a||d>=b) //如果c可以交换或者d可以交换,循环继续
{
if(c>=a)
{
int k=c/a;
c=c%a;
c+=k;
d+=k;
ans+=k;
}
if(d>=b)
{
int k=d/b;
d=d%b;
c+=k;
d+=k;
ans+=k;
}
if(c>=e&&d>=f) //成无限情况
{
puts("INF");
flag=false;
break;
}
e=c,f=d;
}
if(flag)
cout<<ans<<endl;
}
}
F题 全 1 子矩阵
有个坑点,如果该矩阵没有0,一样不符合情况,这题还是很简单的,只需要求出最左上边的
1点和最右下边的1点,然后去查看这个块中是否有0点,如果有就不符合情况
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=20;
char a[N][N];
int n,m;
int main()
{
while(cin>>n>>m)
{
bool fir=false; //判断是否有1点
int sth=n+1,edh=-1,sts=m+1,eds=-1;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
if(a[i][j]=='1')
{
fir=true;
sth=min(sth,i);
edh=max(edh,i);
sts=min(sts,j);
eds=max(eds,j);
}
}
if(!fir) //矩阵没有1,不符合题意
{
puts("No");
continue;
}
bool flag=true;
for(int i=sth;i<=edh;i++)
{
if(!flag) break;
for(int j=sts;j<=eds;j++)
if(a[i][j]=='0')
{
flag=false;
break;
}
}
if(flag) puts("Yes");
else puts("No");
}
}
G题 大水题
可能是签到题?只要知道是容斥原理,这题就过了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
long long n;
while(cin>>n)
{
ll a,b,c,d;
a=n/2;
b=n/5;
c=n/11;
d=n/13;
ll e,f,g,h,i,j;
e=n/10;
f=n/22;
g=n/26;
h=n/55;
i=n/65;
j=n/143; //2 5 11,2 5 13, 2 11 13,5 11 13
ll k,l,m,o;
k=n/110;
l=n/130;
m=n/286;
o=n/715;
ll p=n/1430;
ll ans=a+b+c+d-e-f-g-h-i-j+k+l+m+o-p;
n=n-ans;
cout<<n<<endl;
}
}
H题 神奇的幻方
模拟题,只需要按照题意,便可正确。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=45,M=N*N;
int a[M],b[N][N],c[M];
int n;
int main()
{
cin>>n;
memset(b,0,sizeof b);
b[1][n/2+1]=1;
a[1]=1;
c[1]=n/2+1;
for(int i=2;i<=n*n;i++)
{
if(a[i-1]==1&&c[i-1]!=n)
{
b[n][c[i-1]+1]=i;
a[i]=n;
c[i]=c[i-1]+1;
continue;
}
if(c[i-1]==n&&a[i-1]!=1)
{
b[a[i-1]-1][1]=i;
a[i]=a[i-1]-1;
c[i]=1;
continue;
}
if(a[i-1]==1&&c[i-1]==n)
{
b[2][c[i-1]]=i;
a[i]=2;
c[i]=c[i-1];
continue;
}
if(b[a[i-1]-1][c[i-1]+1]==0)
{
b[a[i-1]-1][c[i-1]+1]=i;
a[i]=a[i-1]-1;
c[i]=c[i-1]+1;
continue;
}
if(b[a[i-1]+1][c[i-1]]==0)
{
b[a[i-1]+1][c[i-1]]=i;
a[i]=a[i-1]+1;
c[i]=c[i-1];
}
}
for(int i=1;i<=n;i++)
{
printf("%d",b[i][1]);
for(int j=2;j<=n;j++)
{
printf(" %d",b[i][j]);
}
cout<<endl;
}
}
I题 骰⼦的游戏
读题后发现是一道简单题,只需要6*6对比每个筛子的输赢情况,然后判断谁赢得多,便可做对。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[10],b[10];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
for(int i=1;i<=6;i++) scanf("%d",&a[i]);
for(int i=1;i<=6;i++) scanf("%d",&b[i]);
int awin=0,bwin=0;
for(int i=1;i<=6;i++)
for(int j=1;j<=6;j++)
{
if(a[i]>b[j]) awin++;
else if(a[i]<b[j]) bwin++;
}
if(awin>bwin) puts("Alice");
else if(awin<bwin) puts("Bob");
else puts("Tie");
}
}
J题 幸运数字Ⅱ
这题大家要记住,如果像这种1000000000的范围的题,就需要用一些减时间的方式去得到整个1到1000000000满足的数(预处理)。然后再去做题,因为许多数的next数都是相同的,所以这些数到next数的next数的值都是相同的,所以每次从a到next[a]的总合就是(next[a]-a+1)*next[a] (next[a]<=r)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
ll a[N];
ll l,r;
ll n;
ll dfs(ll x)
{
if(x>=5000000000) return 0; //到了结果不可能的位置,停止
dfs(x*10+4); //因为x符合题意 x*10+4也符合题意
dfs(x*10+7); //同理
a[++n]=x; //将符合题意的x放入数组中
return 0;
}
int main()
{
n=0; //代表数组中数的个数
dfs(0); //从0开始找符合题意的数
sort(a+1,a+n); //排序
cin>>l>>r;
ll ans=0;
for(ll i=l;i<=r;)
{
ll res=lower_bound(a+1,a+n,i)-a; //二分查找函数,a[res]是数组中比i大的第一个数
if(a[res]>r) ans=ans+(r-i+1)*a[res]; //如果a[res]超过界限,即是取i到r的next
else ans=ans+(a[res]-i+1)*a[res]; //没出界,取i到a[res]的next
i=a[res]+1;
}
cout<<ans<<endl;
}
K题 期末成绩
模拟题,按照题意即可正确,记得输出是一位小数。
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int main()
{
double a,b,c,d;
cin>>a>>b>>c>>d;
double ans=a*0.2+b*0.1+c*0.2+d*0.5;
printf("%.1lf",ans);
}
L题 矩阵消除游戏
开始想错了,以为只是每次从列或行中找一个最大的数即可,后来交题发现只有百分之90的数据过所以发现自己的暴力搜索还不够,少搜索了一些情况,所以我用到了数电中的一些知识。
m列我们可以看成有2^m种拿去可能。
比如m=3,这时情况有 9中 000,001,010,011,100,101,110,111,在这些可能中比如010,就是消除第二列。
而这道题我们可以枚举2^m种可能,每种可能我们可以得到它要我们消除那些列和一共要消除的列的个数cnt,如果个数超过k次,那这种可能是不可能实现的。在我们拿完列的情况后我们还能拿取k-cnt行,这时我们就可以从大到小排序这些行的总权值,从大到小拿出k-cnt行。而在2^m次方种可能性中我们得到得到的最大的数值就是答案。
k如果超过min(n,m),我们就可以直接将所有行或列全拿走,这时我们可以将k=min(k,min(n,m))来避免计算错误。
#include<bits/stdc++.h>
using namespace std;
const int N=110;
typedef long long ll;
int n,m,k;
int a[N][N]; //记录每行每列的数值
int hang[N]; //记录每行的总权值
bool cmp(int a,int b) //自定义排序(从大到小)
{
return a>b;
}
int check(int x) //获得x中1的数量
{
int t=0;
while(x)
{
t++;
int k=~x+1;
k=x&k;
x-=k;
}
return t;
}
int main()
{
ll an=-1;
scanf("%d%d%d",&n,&m,&k);
k=min(k,min(n,m));
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
scanf("%d",&a[i][j]);
for(int i=0;i<(1<<m);i++) //1<<m表示2^m
{
int cnt=check(i); //获得i中1的个数
if(cnt>k) continue; //如果超过k,代表要取多于k次的边,不合理,此情况跳过
ll res=0;
memset(hang,0,sizeof hang); //每种情况,都要清空一下数组hang,因为每次取走一个列,一下行中的数会发生变化,所以每种情况中每条边的权值是不一样的。
for(int j=0;j<n;j++)
for(int l=0;l<m;l++)
if(1<<l&i) res+=a[j][l]; //1<<l&i 表示判断i的二进制数中第L个是否是1,如果是1代表第L列要被消去。
else hang[j]+=a[j][l]; //如果L列不被消去 那么第j行还有a[j][l]。
sort(hang,hang+n,cmp); //按从大到小排出权值最大的行
for(int j=0;j<k-cnt;j++)
{
res+=hang[j]; //取k-cnt行
}
if(an<res) an=res; //得到最大的答案.
}
cout<<an<<endl; //输出
return 0;
}
提示一下如何得到一个数二进制中1的个数,这里用的是lowbit算法
使用lowbit操作,进行,每次lowbit操作截取一个数字最后一个1后面的所有位,每次减去lowbit得到的数字,直到数字减到0,就得到了最终1的个数,
lowbit原理
根据计算机负数表示的特点,如一个数字原码是10001000,他的负数表示形势是补码,就是反码+1,反码是01110111,加一则是01111000,二者按位与得到了1000,就是我们想要的lowbit操作,即:
int lowbit(int x)
{
int t=0;
while(x)
{
int k=~x+1; //获得x的反码+1
k=x&k;
x=x-k;
t++;
}
return t;
}
//如 x=1001000,反码是0110111,加一得0111000,与x相与得1000即第一个1的位置相减得1000000,直到x=0,每减一次t加一次,最后t就是x中1的个数
版权声明:本文标题:NENU 2021 第一阶段新生选拔赛 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dongtai/1728850104a1176566.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论