• 开学考试题2:words(暴力+状压)


    题目:

     注意:

    这里的范围是n*m<=1e5!!!

    分析:

    前面的小数据可以用n^2 * m的暴力过掉(其实后面也可以)

    暴力100

    #include<bits/stdc++.h>
    using namespace std;
    #define N 100005
    #define ll long long
    int m;
    ll t[N],n;
    vector<char> s[N];
    void work1()
    {
        char ch[5]; int a;
        for(int i=1;i<=n;i++){
            scanf("%s",ch);
            if(ch[0]=='x') a=1;
            if(ch[0]=='y') a=2;
            if(ch[0]=='z') a=3;
            t[a]++;
        }
        ll ans=0;
        for(int i=1;i<=3;i++) ans+=t[i]*(t[i]-1)/2;
        printf("%lld
    %lld
    ",n*(n-1)/2-ans,ans);
    }
    void work2()
    {
        for(int i=0;i<=m-1;i++) printf("0
    ");
        printf("%lld
    ",n*(n-1)/2);
    }
    char tt[N];
    int main()
    {
        freopen("words.in","r",stdin);
        freopen("words.out","w",stdout);
        scanf("%lld%d",&n,&m);
        if(m==1) { work1(); return 0; }
        int fl=1;
        for(int i=1;i<=n;i++){
            scanf("%s",tt);
            for(int j=0;j<m;j++)
             s[i].push_back(tt[j]);
            if(i==1) continue;
            for(int j=0;j<s[i].size();j++)
            if(s[i][j]!=s[i-1][j]) { fl=0; break; }
        }
        if(fl) { work2(); return 0; }
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                int cnt=0;
                for(int k=0;k<=m-1;k++)
                 if(s[i][k]==s[j][k]) cnt++;
                t[cnt]++;
            }
        }
        for(int i=0;i<=m;i++) printf("%lld
    ",t[i]);
        return 0;
    }
    /*
    8 1
    x 
    y
    y
    z
    y
    x
    x
    x
    
    4 3
    xyz
    xyz
    zzx
    xzz
    
    4 4
    xyzx
    xyzx
    xyzx
    xyzx
    */
    暴力

    单独处理数据4和数据5,也比较简单。

    但是看了题解发现,对于m较小的点应该用状压dp实现。

    状压dp:

    有三种字母:x,y,z,所以应该用三进制保存字符串的状态,要求的是相似度一定时的对数,所以定义dp[ s ][ k ]为状态为s,相似度为k的对数。

    初始化:dp[ x ][ m ]=s[ x ]    s是原串。

    转移:dp[ s' ][ k-1 ]+=dp[ s ][ k ]  ,s'是改变1个字符后的状态,改变了一个字符,相似度就应该-1。

    但是会发现,对于这组数据:xyz,xyy,xyx

    dp[ xyy ][ 2 ]+=dp[ xyz ][ 3 ];(枚举xyz时把z改成y去更新xyy相似度为2的方案)

    dp[ xyx ][ 1 ]+=dp[ xyy ][ 2 ];(枚举xyy时把y改成x去更新xyx相似度为1的方案)

    明显dp[ xyx ][ 1 ]==0 ,但是通过上述顺序更新处理=1。

    是哪里出了问题呢?因为我们把xyz的第三位改变了两次,当成了整个串改变了两个位置!!!

    所以应该固定每一个串改变的位置(即最外层枚举要修改的位置),并且一个值被改变后,不能在同一层循环里面再去更新别的值。

    所以要用一个ff数组临时储存一下再来转移

    代码细节很多。。。

    下面是std,自己并没有打

    void Work1(int n, int m) {
        for (int i = 1; i <= n; i++) {
            int now = 0;
            for (int j = 1; j <= m; j++) {
                now *= 3;
                char k;
                for (k = getchar(); k <= 32; k = getchar());
                now += k - 'x';
            }
            ss[now]++;
        }
        mi3[0] = 1;
        for (int i = 1; i <= m; i++)    mi3[i] = mi3[i - 1] * 3;
        for (int i = 0; i < mi3[m]; i++)    dp1[i][m] = ss[i];
        for (int i = 0; i < m; i++) {
            memcpy(dp, dp1, sizeof dp);
            for (int j = 0; j < mi3[m]; j++) {
                int s1 = j / mi3[i] % 3, s2 = j - s1 * mi3[i];
                for (int t = 0; t < 3 * mi3[i]; t += mi3[i])
                    if (t != s1 * mi3[i])
                    for (int p = m - i; p <= m; p++)    dp[s2 + t][p - 1] += dp1[j][p];
            }
            memcpy(dp1, dp, sizeof dp);
        }
        for (int i = 0; i < mi3[m]; i++)
            for (int p = 0; p <= m; p++)
                ans[p] += 1LL * ss[i] * dp1[i][p];
        ans[m] = ans[m] - n;
        for (int i = 0; i <= m; i++)    ans[i] /= 2;
    }
    std
  • 相关阅读:
    iOS网络开发之AFNetworking
    自定义博客园样式
    win 7 和 winserver 2008 下,布署网站遇到的错误解决方法
    iOS开发--沙盒
    毫秒必争,前端网页性能最佳实践
    C#可扩展数组转变为String[]数组
    iOS 界面启动时,功能新特征显示
    批量导入数据到mssql数据库的
    MongoDB 工具助手类(.NET)
    Xcode 快捷键及代码格式化
  • 原文地址:https://www.cnblogs.com/mowanying/p/11482018.html
Copyright © 2020-2023  润新知