现在处于一个混乱的{ }中间态,有时换行有时不换行...
换行确实看起来更简洁。但是不换行看起来更完整。怪不得学C的能为这个问题吵起来。
先把必做题做了,就是抓住了主要矛盾
A - Link/Cut Tree
题目描述
参考代码
#include<iostream>
#include<cmath>
using namespace std;
#define ll long long
ll getAns(ll k,ll p){
ll ans=1;
for(int i=1;i<=p;i++)
ans*=k;
return ans;
}
int main()
{
ll l,r,k,p;
while(cin>>l>>r>>k)
{
p=0;
bool legal=false;
while(true){
if(pow(k,p)>r)
break;
if( pow(k,p)>=l && pow(k,p)<=r ){
cout<<getAns(k,p)<<" ";
legal=true;
}
p++;
}
if(!legal)
cout<<-1<<endl;
}
}
代码讲解
妙处
我觉得第一个巧妙点在#define ll long long 毕竟long long写起来太长了。
第二个看起来笨,但实际上巧妙的是单独用一个函数算出ans值。
两个容易踩的坑
1.不能直接输出pow的结果,因为那会用科学计数法来表示。
cout<<pow(k,p)<<" ";
2.不能先乘再看符不符合条件,ans *= k也超过 long long了!
C - Maximum Product
题目描述
参考代码
#include<iostream>
#include<cmath>
using namespace std;
#define ll long long
int main(){
int n;
int ncase=0;
while(cin>>n){
ll ans=0;
ll num[100];
for(int i=0;i<n;i++){
cin>>num[i];
}
for(int i=0;i<n;i++)
{
for(int j=0;i+j<n;j++){
ll tmp=1;
for(int k=0;k<=j;k++)
tmp*=num[i+k];
if(tmp>ans)
ans=tmp;
}
}
if(ans<0)
ans=0;
printf("Case #%d: The maximum product is %lld.\n\n",++ncase,ans);
}
}
代码讲解
核心是两个for循环,遍历可能的区间。
for(int j=0;i+j<n;j++)挺妙的,尤其是i+j<n,保证j在i后并且能所有都遍历到。
还有一个妙处在 if(ans<0) ans=0;直接把所有可能是负值的情况在最后解决了,不用修改中间的算法来加一些判断。
E - 组合的输出
题目描述
参考代码
#include<iostream>
#include<vector>
#include<algorithm>
#include<iomanip>
using namespace std;
bool cmp(vector<int> a,vector<int> b){
for(int i=0;i<a.size();i++)
{
if(a[i]!=b[i])// 这一步不可少
return a[i]<b[i];
}
}
int main(){
int n,r;
cin>>n>>r;
vector<vector<int> > res;
for(int i=0;i<(1<<n);i++){
int cnt=0;
for(int j=0;j<=n;j++){
if(i&(1<<j)) {
cnt++;
cout << "i:" << i << endl;
cout << "1<<j:" << (1 << j) << endl;
cout<<"cnt:"<<cnt<<endl;
}
}
if(cnt==r){
vector<int> tmp;
for(int j=0;j<=n;j++){
if(i&(1<<j)){
tmp.push_back(j+1);
}
}
res.push_back(tmp);
}
}
sort(res.begin(),res.end(),cmp);
for(vector<int> a : res){
for(int b : a){
cout<<setw(3)<<b;
}
cout<<endl;
}
}
代码讲解
这个真的是对位运算的巧妙应用。
就假设输入是 5 3 吧。
那选3个就是就是要在五个数中选出三个不同的数,在二进制中,就是五位中要有三个一。
遍历2^n,对于一个特定的i,就比如7,就是01011。此时j从0~n-1遍历一遍,就是把i转成2进制的每一位扫一遍,如果该位是1就让cnt+1。
如果cnt=r,说明这个i对应的数刚好是选出的r位数。
此时把这个答案放进一个临时的数组中,tmp.push_back(j+1)把从0开始索引改成从1开始索引。
最后的答案会有C(n,r)个。
i&(1<<j)
i&(1<<j)
是一个位操作,用于检查整数 i
的第 j
位是否为 1。这种操作在组合、子集生成或其他位操作相关的算法中很常见。
我们使用位操作符 &
(与操作)来执行这个检查。在这个操作中,我们将数字 1
左移 j
位,从而创建了一个只在第 j
位为 1 的数字(其他位都为 0)。然后,我们使用 &
将此数字与 i
进行比较,如果结果不为 0,则表示 i
的第 j
位为 1,否则该位为 0。
让我们通过一些例子来说明:
-
检查数字
i = 5
的第1
位(从0开始计数,即第二个位):- 首先,我们知道
5
在二进制中表示为101
。 - 使用操作
1<<1
,我们将数字1
左移一位得到10
,即2
。 - 现在,我们将
5
(即101
)与2
(即010
)进行&
运算,得到0
。这意味着数字5
的第1
位为 0。
- 首先,我们知道
-
检查数字
i = 5
的第2
位(从0开始计数,即第三个位):- 首先,我们知道
5
在二进制中表示为101
。 - 使用操作
1<<2
,我们将数字1
左移两位得到100
,即4
。 - 现在,我们将
5
(即101
)与4
(即100
)进行&
运算,得到4
。这不是0
,所以数字5
的第2
位为 1。
- 首先,我们知道