洛谷刷题记录---------暴力枚举

统计方形

对于一个 n*m 的矩形,他所含的所有矩形的数量为 n ∗ m n*m nm,矩形数=正方形数 + 长方形数,对于正方形,我们枚举每个顶点作为右下方的顶点,数量为 m i n ( i , j ) min(i,j) min(i,j),然后长方形的数量就等于总数减去正方形的数量。

#include<bits/stdc++.h>
#define endl '\n'

using namespace std;

using ll = long long;

void solve()
{
    ll n,m,ans1=0,ans2=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            ans1+=min(i,j); //正方形
            ans2+=i*j-min(i,j); //长方形 = 总数 - 正方形
        }
    }
    cout<<ans1<<" "<<ans2<<endl;
    return ;
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T; T=1;
    while(T--) solve();
    return 0;
}

子集枚举

选数

让每个子集对应一个二进制数,例如五个元素的集合,00000代表空集,从左到右为第 1 - 5 个元素。
非空子集的个数: 2 n − 1 2^n-1 2n1
构建只含某个输的集合: 1 < < ( i − 1 ) 1<<(i-1) 1<<(i1)
并集:两个二进制数做或运算。
交集:两个二进制数做与运算。
包含:集合 A 1 A_1 A1 包含集合 A 2 A_2 A2 时,两个集合的交集为 A 2 A_2 A2 并集为 A 1 A_1 A1,即 ( A 1 ∣ A 2 = = A 1 ) & & ( A 1 & A 2 = = A 2 ) (A_1|A_2==A_1) \&\& (A_1 \& A_2==A2) (A1A2==A1)&&(A1&A2==A2)
属于:判断某个元素是不是在集合中,是包含的特殊情况,先构造出只含这个元素的集合再用包含的方式进行判断即可。
补集:异或运算,同为0,不同为1。
函数:__builtin_popcount() 能返回一个数二进制下 1 的个数。

#include<bits/stdc++.h>
#define endl '\n'

using namespace std;

const int maxn = 23;
int a[maxn];

bool check(int x)
{
    for(int i=2;i<=x/i;i++)
    if(x%i==0) return false;
    return true;
}
void solve()
{
    int n,k; cin>>n>>k;
    int ans=0;
    for(int i=1;i<=n;i++) cin>>a[i];
    int u=1<<n; // u-1就是全集
    for(int s=0;s<u;s++) //枚举子集
    {
        if(__builtin_popcount(s)==k) //找到 k 元子集
        {
            int sum=0;
            for(int i=1;i<=n;i++)
            if(s&(1<<i-1)) sum+=a[i];
            if(check(sum)) ans++;
        }
    }
    cout<<ans<<endl;
    return ;
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T; T=1;
    while(T--) solve();
    return 0;
}

组合的输出
正着枚举无法保证次序,一种可行的方案是倒着枚举,往里面存 0 到 n-1,然后再倒序输出 n - a[i],具体看代码。

#include<bits/stdc++.h>
#define endl '\n'

using namespace std;

const int maxn = 23;
int a[maxn];

void solve()
{
    int n,r; cin>>n>>r;
    int u=1<<n;
    for(int s=u;s>=0;s--)
    {
        int cnt=0;
        for(int i=0;i<n;i++)
        if(s&(1<<i)) a[++cnt]=i;
        if(cnt==r) 
        {
            for(int i=cnt;i>=1;i--)
            cout<<setw(3)<<n-a[i];
            cout<<endl;
        }
    }
    return ;
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T; T=1;
    while(T--) solve();
    return 0;
}

全排列问题

  • next_permutation() 函数用于生成容器或序列的下一个字典序排列。在 algorithm 库中。
bool next_permutation(BidirectionalIterator first, BidirectionalIterator last, Compare comp);
// 起始迭代器,自定义顺序,默认升序
// 成功生成返回 true 
// 如果当前排列已经是字典序的最后一个排列,则返回 false,并生成字典序的第一个排列(即最小排列)
#include<bits/stdc++.h>
#define endl '\n'

using namespace std;

const int maxn = 13;
int a[maxn];

void solve()
{
    int n; cin>>n;
    for(int i=1;i<=n;i++)
    a[i]=i;
    do
    {
        for(int i=1;i<=n;i++)
        cout<<setw(5)<<a[i];
        cout<<endl;
    } while (next_permutation(a+1,a+1+n));
    return ;
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T; T=1;
    while(T--) solve();
    return 0;
}

小 Y 拼木棒

由于强制用四根木棒,因此一定是两根相同的加上另外两根。观察到 a i a_i ai 在 5000 以内,因此开一个桶来记录每个长度的木棒的数量。当另外两根木棒的长度不同时,对答案的贡献为 C f [ i ] 2 ∗ f [ j ] ∗ f [ i − j ] C_{f[i]}^{2}*f[j]*f[i-j] Cf[i]2f[j]f[ij] 当另外两根木棒长度也相同时,对答案的贡献为 C f [ i ] 2 ∗ C f [ j ] 2 C_{f[i]}^{2}*C_{f[j]}^{2} Cf[i]2Cf[j]2

#include<bits/stdc++.h>
#define int long long
#define endl '\n'

using namespace std;

const int maxn = 1e5+3;
const int mod = 1e9+7;
int a[maxn],n,ans;
int f[maxn]; // 桶

void solve()
{
    cin>>n;
    int minn=1e9,maxn=-1e9;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i],f[a[i]]++;
        minn=min(minn,a[i]);
        maxn=max(maxn,a[i]);
    }
    for(int i=minn+1;i<=maxn;i++) //由于必须选四根,因此从 minn+1 开始
    {
        if(f[i]<=1) continue; // 不可能组成三角形
        for(int j=minn;j<=i/2;j++)
        {
            if(f[j]&&f[i-j]) // 能组成三角形
            {
                if(j!=i-j) // 两数不同
                ans+=((f[i]*(f[i]-1)/2)%mod)*(f[j]*f[i-j]%mod)%mod;
                else if(f[j]>=2) // 两数相同
                ans+=(((f[i]*(f[i]-1))/2)%mod)*((f[j]*(f[j]-1)/2)%mod)%mod; 
            }
        }
    }
    cout<<ans%mod<<endl;
}

signed main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T; T=1;
    while(T--) solve();
    return 0;
}
```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值