• 模拟赛1 题解


    从今天开始写写套题的题解,与单题题解分开。
    模拟赛题解的话按照我写的顺序从 1 开始标号,不代表实际的顺序 orz。

    这一场好像是 DP 专练啊……DP 题挺多。


    我太菜了,有些题还不是很明白,所以这篇先占着坑吧……

    CF1238D AB-string

    这道题要计算好串,不妨先看看好串的规律,发现只要好串中存在A...B...AB...A...B形式的子串,那么它一定是好串,这样计算比较困难。我们考虑正难则反,计算坏串,坏串的规律也十分明显,只要是形如B...BA A...AB AB...B BA...A的都是坏串,那么我们计算坏串实际上就等价于计算正反两遍连续相同字母段的个数(因为每一个连续的相同字母段中的每一个都可以和下一段或上一段组成一个坏串),不过注意这样计算的话ABBA这种串其实多算了一遍,最后要补回来。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=3e5+5;
    int n,cnt[N],tot;
    char s[N];
    
    int main()
    {
        scanf("%d %s",&n,s+1);
        s[0]='C';
        for(int i=1;i<=n;++i)
            if(s[i]==s[i-1]) ++cnt[tot];
            else cnt[++tot]++;
        long long ans=1LL*n*(n-1)/2;
        for(int i=1;i<tot;++i) ans-=cnt[i];
        for(int i=tot;i>1;--i) ans-=cnt[i];
        printf("%lld
    ",ans+tot-1);
        return 0;
    }
    

    CF1238E Keyboard Purchase

    这题没懂……(

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=1e5+5,M=20,U=1<<20;
    int n,m,f[U],g[N],dis[M][M];
    char s[N];
    
    int main()
    {
        scanf("%d%d %s",&n,&m,s+1);
        for(int i=1;i<n;++i)
            ++dis[s[i]-'a'][s[i+1]-'a'],
            ++dis[s[i+1]-'a'][s[i]-'a'];
        for(int S=0;S<(1<<m);++S)
            for(int i=0;i<m;++i)
                if(S>>i&1) for(int j=0;j<m;++j)
                    if(!(S>>j&1)) g[S]+=dis[i][j];
        memset(f,0x3f,sizeof(f));
        f[0]=0;
        for(int S=0;S<(1<<m);++S)
            for(int i=0;i<m;++i)
                if((S>>i&1)) f[S]=min(f[S],f[S^(1<<i)]+g[S^(1<<i)]);
        printf("%d
    ",f[(1<<m)-1]);
        return 0;
    }
    

    CF1239E Turtle

    这道题官方题解写得很好。首先有几个结论:最小值和次小值一定要放到起点和终点;如果第一行和第二行的数已经确定,那么最优答案必然是第一行升序排序,第二行降序排序。第一个比较显然,第二个要稍微证证。考虑对当前第一行相邻两格的数,如果左边的数比右边的数大,交换它们两个的位置后,答案一定不会变差(有的路总和不变,有的路总和变小),那么我们按照冒泡排序的思想,第一行排好序显然是最优情况,按照同样的逻辑第二行也可以推出,只不过第二行刚好反过来。
    在之前的前提下继续讨论,为了方便接下来的分析,我们按照每条路拐的位置为每条路编号(在第一行第 (i) 格拐下去的路称为第 (i) 条路)。考虑第 (i) 和第 (i+1) 条路的分值差 (Delta=a_{1,i+1}-a_{2,i})(Delta) 的函数图像是一个下凸壳,最大值在两边取到。于是问题转化为:将 (2(n-1)) 个数分为相同大小的两组,使得两组总和的最大值最小。这是一个 01 背包问题,记 (f(i,j,k)) 表示在前 (i) 个数中选 (j) 个数能否组成 (k),转移:(f(i,j,k)=[f(i-1,j-1,k-a_i)=1])。答案记录路径输出即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=55,M=1e6+5;
    int n,Max,a[N],ans[2][N],sum,bur[M],pre[N][M];
    bool f[N][M];
    
    void write(int x)
    {
        int now=n-1,tot=1;
        for(int i=1;i<n;++i)
            ans[1][i]=pre[now][x],--bur[pre[now][x]],x-=pre[now][x],--now;
        for(int i=0;i<=Max;++i)
            while(bur[i]--) ans[0][++tot]=i;
        for(int i=1;i<=n;++i) printf("%d ",ans[0][i]);
        puts("");
        for(int i=1;i<=n;++i) printf("%d ",ans[1][i]);
        puts("");
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=2*n;++i) 
            scanf("%d",&a[i]),Max=max(a[i],Max),sum+=a[i],++bur[a[i]];
        sort(a+1,a+2*n+1);
        ans[0][1]=a[1],ans[1][n]=a[2],f[0][0]=1;
        sum=(sum-a[1]-a[2])/2; --bur[a[1]],--bur[a[2]];
        for(int i=3;i<=2*n;++i)
            for(int j=min(i,n-1);j;--j)
                for(int k=sum;k>=a[i];--k)
                    if(!f[j][k]&&f[j-1][k-a[i]])
                        pre[j][k]=a[i],f[j][k]=1;
        for(int i=sum;~i;--i)
            if(f[n-1][i]) {write(i); return 0;}
        return 0;
    }
    

    [HNOI2010]公交线路

    这题不会……啊啊啊状压杀我(物理上
    UPD:这题我已经会了,但懒得补了……就这样吧……

  • 相关阅读:
    loj 1251(2-sat + 输出一组可行解)
    hdu 4751(dfs染色)
    hdu 2545(并查集求节点到根节点的距离)
    uva 10972(边双连通分量)
    uva 10246(最短路变形)
    uva 11380(最大流+拆点)
    hdu 4640(状压dp)
    hdu 1430+hdu 3567(预处理)
    python基础知识回顾[1]
    基于websocket搭建简易群聊
  • 原文地址:https://www.cnblogs.com/wzzyr24/p/12618416.html
Copyright © 2020-2023  润新知