补题篇 2021--11-9

给你 nn 个字符串,每个字符串最多包含 A - ZA−Z 这26个字母,KevenKeven 现在取了一些字符串,发现每个字母出现的次数都是 3 的倍数,KevenKeven 现在想要知道在满足每个字母出现的次数都是 3 的倍数的前提下,最多能取多少个字符串。

是真的没想到可以用dfs做

#include <bits/stdc++.h>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define ll long long
using namespace std;
int n, m;
int a[15][26] = {0};
int c[26] = {0};
void bfs(int x, int k){
	if(x >= n){
		for(int i = 0; i < 26; i++) if(c[i] % 3) return;
		m = max(m, k);
		return;
	}
	for(int i = 0; i < 26; i++) c[i] += a[x][i];
	bfs(x + 1, k + 1);
	for(int i = 0; i < 26; i++) c[i] -= a[x][i];
	bfs(x + 1, k);
}
int main(){
	string b;
	m = 0;
	cin >> n;
	for(int i = 0; i < n; i++){
		cin >> b;
		for(int j = 0; j < b.size(); j++){
			a[i][b[j] - 'A']++;
		}
	}
	bfs(0, 0);
	cout << m << endl;
	return 0;
}

即每个字符串都有      选与不选两种可能,穷尽所有情况。

#include<bits/stdc++.h>
#define lowbit(x) (x & -x)
using namespace std;
int t, n;
int main() {
    scanf("%d", &t);
    while(t --) {
        scanf("%d", &n);
        int ans = 0;
        while(n) {
            int x = n, gs = 0;
            int ha = lowbit(x), haa = 0;
            while(x) {
                if(x == lowbit(x)) haa = lowbit(x);    //只剩下最高位的时候。
                x -= lowbit(x);
                gs ++;
            }
            if(gs & 1) n ^= 1;
            else n -= haa;
            ans ++;
        }
        printf("%d\n", ans);
         
    }
    return 0;
}

 末尾取反运用了异或1的操作。

 lowbit:它的作用是求出n在表示成二进制的时候,最右边的1出现的位置对应的数

跳跳跳

#include<iostream>
using namespace std;

int n;
int dp[4100][4100], a[4100];
int dfs(int l,int r,int time)
{
    if(l==r) dp[l][r]=a[l]*time;
    if(!dp[l][r])
        dp[l][r] = max(time*a[l] + dfs(l+1, r, time+1), time*a[r] + dfs(l, r-1, time+1));
   return dp[l][r];     
}

int main()
{
     scanf("%d",&n);
     for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i+n]=a[i];
    int ans=0;
    for(int i = 1;i <= n;i ++)
        ans = max(ans, dfs(i, i+n-1, 1));
    cout<<ans<<endl;
   
     
     
    return 0;
}

想写dfs枚举所有情况结果理解错了题目的意思,本题并不是说往右跳了就不能往左跳,而是可以往左跳到还未被跳过的点,看了李大佬的答案后豁然开朗,即把问题划分成子问题,先求小区间最值,小区间被遍历后,因为可以往左或往右跳到还未跳到的点,就可以往左或者往右扩大最值。                                                  

数字匹配

很巧妙的做法是锤神使用的 32-__builtin_clz()函数,这里提供模拟做法

#include <bits/stdc++.h>
#define ll long long
#define MAXN 4005
#define Inf 0x3f3f3f3f
 
using namespace std;
 
int n,k;
int numx[30],numy[30];
 
bool check(int x, int y)
{
    memset(numx,0,sizeof numx);
    memset(numy,0,sizeof numy);
    int cntx=0, cnty=0;
    while(x)
    {
        numx[++cntx] = x%2;
        x/=2;
    }
    while(y)
    {
        numy[++cnty] = y%2;
        y/=2;
    }

     for(int s=1;cnty+1-s>=k;s++)
     {
         int sum=0;
         for(int i=1;i<=cntx&&s+i-1<=cnty;i++)
         {
             if(numx[i]==numy[s+i-1])
                 sum++;
                 else
                 {
                     sum=0;
                 }
             if(sum>=k) return true;
         }
         
     }
    
    for(int s=1;cntx+1-s>=k;s++)
     {
         int sum=0;
         for(int i=1;i<=cnty&&s+i-1<=cntx;i++)
         {
             if(numy[i]==numx[s+i-1])
                 sum++;
                 else
                 {
                     sum=0;
                 }
             if(sum>=k) return true;
         }
         
     }

 
    return false;
}
 //可能x大,也可能y大
int main()
{
    int ans = 0;
    scanf("%d%d",&n,&k);
    for(int i=1; i<n; i++)
    {
        for(int j=i+1; j<=n; j++)
        {
            if(i<j)
            ans += check(i,j);
            else 
              ans += check(i,j);
        }
    }
    printf("%d\n",ans);
 
    return 0;
}

即使用数组先使两个数之一滑动与另一数匹配,接着滑动另一个数去匹配。

分组

使用二分查找答案,按hammer的思路,就是如果51可以那么52,53也可以,通过二分可以确定答案。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1e5+10;
int a[N], n, m;
 


bool check(int x)
{
    int sum=0;
    for(int i=1;i<=n;i++) sum+=(a[i]-1+x)/x;
    return sum>m;
}
int main()
{
    cin>>n>>m;
    for(int i = 1, j;i <= n;i ++)
    {
        scanf("%d", &j);
        a[j] ++;
    }
    sort(a+1, a+n+1, greater<int>());
    while(!a[n])n --;
    
    if(n>m) 
    {
       
        cout<<-1;
        return 0;
    }
   // cout<<-12312321;
    int l=1,r=N;
    while(l<r)
    {
        int mid=(l+r)/2;
        if(check(mid))   l=mid+1;
        else r=mid;
    }
    cout<<l<<endl;
    
    
    
    return 0;
}

快乐风男

#include<string>
#include<iostream>
#include<math.h>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e5+5;
int a[maxn],len[maxn],dp[maxn],res[maxn];
int cnt;
int n;
//dp[i]记录当前字符串结尾的最长非递减(本题为递增子序对的个数。
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
     memset(len,0,sizeof(len));
    dp[1]=a[1],len[1]=1;
    cnt=1;
    for(int i=2;i<=n;i++)
    {
        if(a[i]>dp[cnt])
        {
            cnt++;
            len[i]=cnt;
            dp[cnt]=a[i];
           
        }
        
        else 
        {
           int pos=lower_bound(dp+1,dp+1+cnt,a[i])-dp;
           len[i]=pos;
           dp[pos]=a[i];
        }
      
    }
     memset(res,0,sizeof(res));

    for(int i=n;i>=1;i--)
    {
        if(len[i]==cnt||a[res[len[i]+1]]>a[i])
        {
            res[len[i]]=i;
        }
    }
        cout<<cnt<<endl;
     for(int i=1;i<cnt;i++)
        cout<<res[i]<<" ";
    cout<<res[cnt]<<endl;
    
    
    
    
    return 0;
}

使用二分搜索更新值,对于dp数组,记录的是以当前字符结尾的连续序列的长度是多少。

最后在查找最小字典序的是时候,逆序更新数组,即两个下标如果位置一样的情况,在前面的下标一定会把在后面的下标更新。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值