暑期集训2期3

简单题在这里插入图片描述

直接枚举−3000到3000也没关系。
枚举最后的x,容易发现x 的取值不会在序列取值之外。
不开long long 会挂掉30分。

#include<bits/stdc++.h>
#define inf pow(2,64)
using namespace std;
long long n,a[3500];
long long mans=-inf,mians=inf;
unsigned long long ans=inf;
inline int read()
{
    int f=1;
    int res=0;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))res=(res<<1)+(res<<3)+(ch&15),ch=getchar();
    return res*f;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)a[i]=read(),mans=max(mans,a[i]),mians=min(mians,a[i]);
     
    for(int i=mians;i<=mans;i++)
    {
        unsigned long long tot=0;
        for(int j=1;j<=n;j++)
        tot+=(i-a[j])*(i-a[j]);
         
        ans=min(ans,tot);
    }
    cout<<ans;
    return 0;
}


移动杠杆

在这里插入图片描述
题目大意
两排数,你的目标是将数值相同的放到一起。
滚动不消耗代价。
提起消耗的代价为数值。
你需要最小化提起的数值限制,也就是移动的最大值尽可能小
30分算法:
一旦一个数值使用了第二种操作,可以直接把它看成消失了,因为可以不对剩下的数造成任何阻碍。
枚举每个数是否使用第二种操作,然后 check 一下剩下的数对中间是否都没有障碍数。
时间复杂度O(a2n),期望得分30。
但是你只要想出了第一个方法,你一般也就不会只有30分,但这都是后话。
60~80分算法:
考虑一对数,如果它们跨排,那么一定会使用第二种操作。
如果在同排,达到目标有两种方案:自身使用第二个操作或是中间的所有数都使用了第二个操作。
• 问题转化为了区间取max。直接循环扫,期望得分60∼80。
100分算法1:
使用一些方法优化到O(nlogn) 就可以获得满分。

#include<bits/stdc++.h>
#define maxn 1000010
using namespace std;
inline int read()
{
    int f=1;
    int res=0;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))res=(res<<1)+(res<<3)+(ch&15),ch=getchar();
    return res*f;
}
int n,nans[maxn],top;
int a[maxn],b[maxn];
int ans;    
int A[maxn],B[maxn];
inline bool check(int x)
{
 
    int t1=0,t2=0;
    for(int i=1;i<=n;i++)if(a[i]>x)A[++t1]=a[i];
    for(int i=1;i<=n;i++)if(b[i]>x)B[++t2]=b[i];
    if(t1==1||t2==1)return 0;
     
    for(int i=2;i<=t1;i++)if(A[i]!=A[i-1]&&A[i]!=A[i+1])return 0;
    for(int i=2;i<=t2;i++)if(B[i]!=B[i-1]&&B[i]!=B[i+1])return 0;
    return 1;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
    //  mans=max(mans,a[i]);
        //mians=min(mians,a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        b[i]=read();
    //  mans=max(mans,b[i]);
    //  mians=min(mians,b[i]);
    }
    long long l=0,r=1e9;
    while(l<=r)//二分
    {
        int mid=(l+r)>>1;     
        if(check(mid))
        {
            r=mid-1;
            ans=mid;
        }
        else l=mid+1;
    }
    cout<<ans;
    return 0;
}


串串

在这里插入图片描述

40分算法:
一看就是dp
• 令fi表示以 i 结尾的前缀的答案。枚举j<i,如果sj+1 ∼ i是好的字符串那么就用fj+1更新fi。
• O(n)级别的 check 期望得分40。
60~100分算法:
• 从后往前枚举j,边枚举边维护,可以做到总复杂度O(n2)。加点剪枝,期望得分60∼100。

#include<bits/stdc++.h>
#define inf 0x7fffffff
#define maxn 200005
using namespace std;
inline int read()
{
    int f=1;
    int res=0;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))res=(res<<1)+(res<<3)+(ch&15),ch=getchar();
    return res*f;
}
int n,vis[35],dp[maxn],cont[maxn];
string s;
int main()
{
    n=read();cin>>s;
    memset(dp,127,sizeof(dp));
    s=" "+s;
    int len=s.size();
    dp[0]=0;
    for(int i=1;i<len;i++)
    {
        int sum=0;
 
        for(int j=1;j<=26;j++)cont[j]=0;
        for(int j=i;j>=0;j--)
        {
            if(cont[s[j]-'a'+1]==0)sum++;
            cont[s[j]-'a'+1]++;
            if(sum==n)dp[i]=min(dp[j-1]+1,dp[i]);
            if(sum>n)break;
        }
        if(dp[i]>1e6)printf("%d\n",-1);
        else printf("%d\n",dp[i]);
    }
    return 0;
}


天天

在这里插入图片描述
30分算法:
直接暴力枚举所有可能的 01 串,并计算它与每个已有 01 串的距离,期望得分30。

基于原来30分的算法。
对于20% 的特殊性质数据部分,直接输出1 2 即可。
100分算法:
将题目看成是一张 2m 个点的图,读入的 n−1 个 01 串是起点。
每个点都与与自己只差一位的点有连边,边权为1。
要找 dist 最大的点,并输出个数。
直接 bfs 即可获得满分。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int f=1,res=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))res=(res<<1)+(res<<3)+(ch&15),ch=getchar();
    return res*f;
} 
int ans,tot;
string s; 
int n,m,vis[100010],dp[100010];
queue<int>q;
int main()
{
    n=read();m=read();
    for(int i=1;i<n;i++)
    {
        int k=0;
        cin>>s;
        for(int j=0;j<m;j++)
        k=(k<<1)+(s[j]&15);
        if(!vis[k])q.push(k);
        vis[k]=1;
    }
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=0;i<m;i++)
        {
            int ne=x^(1<<i);
            if(!vis[ne])
            {
                q.push(ne);
                vis[ne]=1;
                dp[ne]=max(dp[ne],dp[x]+1);
                if(dp[ne]>ans)ans=dp[ne],tot=1;
                else if(dp[ne]==ans)tot++;
            }
        }
    }
    cout<<m-ans<<' '<<tot<<endl;
    return 0;
}

文献参考:参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值