A - Link/Cut Tree,C - Maximum Product,E - 组合的输出 题目描述+参考代码+代码讲解——中山大学软件工程学院专选算法课第三次实验 必做题

现在处于一个混乱的{ }中间态,有时换行有时不换行...

换行确实看起来更简洁。但是不换行看起来更完整。怪不得学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。

让我们通过一些例子来说明:

  1. 检查数字 i = 5 的第 1 位(从0开始计数,即第二个位):

    • 首先,我们知道 5 在二进制中表示为 101
    • 使用操作 1<<1,我们将数字 1 左移一位得到 10,即 2
    • 现在,我们将 5(即 101)与 2(即 010)进行 & 运算,得到 0。这意味着数字 5 的第 1 位为 0。
  2. 检查数字 i = 5 的第 2 位(从0开始计数,即第三个位):

    • 首先,我们知道 5 在二进制中表示为 101
    • 使用操作 1<<2,我们将数字 1 左移两位得到 100,即 4
    • 现在,我们将 5(即 101)与 4(即 100)进行 & 运算,得到 4。这不是 0,所以数字 5 的第 2 位为 1。

猜你喜欢

转载自blog.csdn.net/weixin_64123373/article/details/132967688