zoukankan      html  css  js  c++  java
  • Moortal Cowmbat

    2020-07-06 个人赛1 H:Moortal Cowmbat


     题意:

    样例:


    题解:

    ①题目文本中已经提示说,i->j直接变化的代价不一定是最小的,所以可以借助中间点,先跑一个Floyd,算出两点之间真正的最小代价备用。

    ②设置数组dp[i][j]表示在第i个字母为j时,前i个字母整体的最小代价。

    ③设置数组w[i][j]表示前i位都变成字母j的总代价,这样方便利用前缀和优化

    dp分析:

    ①首先,如果总个数少于2k-1,那只有一种可能,便是所有字母都一样。

    ②如果多于i>2k-1,当第i位为j时,dp[i][j]=min(dp[i-1][j]+改变成j的代价,min(dp[i - k][字母]+ w[i][j] - w[i - k][j]))

    含义:第i个字母为j时,前i个字母整体的最小代价=min(第i-1个字母为j第i个字母也为改变为j的整体代价,(前i-k这段以"各种字母"结尾代价的最小值)+(从第(i-k+1)位到第i位字母全部变成j的代价))。


    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 100000 + 10;
    int n, m, k;
    string s;
    int Map[30][30];///Map[i][j]表示字母i变成字母j的代价
    int w[maxn][30];
    ///w[i][j]表示前i个都变成字母j的代价
    ///w[i][j] = w[i - 1][j] + Map[s[i - 1] - 'a' + 1][j]
    int dp[maxn][30];
    ///dp[i][j]表示在前i个中,第i个位置为字母j的最小代价
    ///此时要保证字符串是k连击 所以只存在以下两种情况
    ///1 i-1为字母j
    ///2 i-k+1 到 i 全都是字母j
    
    int main()
    {
        cin >> n >> m >> k;
        cin >> s;
        for (int i = 1; i <= m; i++)
            for (int j = 1; j <= m; j++)
                cin >> Map[i][j];
        for (int k = 1; k <= m; k++)
            for (int i = 1; i <= m; i++)
                for (int j = 1; j <= m; j++)
                    Map[i][j] = min(Map[i][j], Map[i][k] + Map[k][j]);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                w[i][j] = w[i - 1][j] + Map[s[i - 1] - 'a' + 1][j];
        for (int i = 1; i <= min(n, 2 * k - 1); i++)///长度小于2*k-1 只能是同一个字母
            for (int j = 1; j <= m; j++)
                dp[i][j] = w[i][j];
        for (int i = 2 * k; i <= n; i++)
            for (int j = 1; j <= m; j++)
            {
                ///直接和上一位相同
                dp[i][j] = dp[i - 1][j] + Map[s[i - 1] - 'a' + 1][j];
                for (int c = 1; c <= m; c++)
                    dp[i][j] = min(dp[i][j], dp[i - k][c] + w[i][j] - w[i - k][j]);///加上i-k+1到i都是j的代价
            }
        int ans = 1e9 + 7;
        for (int i = 1; i <= m; i++)
            ans = min(ans, dp[n][i]);
        cout << ans << endl;
        return 0;
    }

    以上代码时间复杂度O(n*m*m),完全可以过ac。

    但其实还可以进一步优化:

    因为我们发现,我们在最后在i,j的双重for循环中,还有一个遍历前i-k这段以"各种字母"结尾代价的最小值的循环。这个循环可以被优化掉。

    我们在处理2k-1个的时候,可以用一个Min[i]表示前i个位置最小的代价。

    这样,在之后dp的转移中,我们不需要遍历前i-k这段以"各种字母"结尾代价的最小值,直接用Min[i-k]即可,在dp转移完之后,顺带更新Min[i] = min(Min[i], dp[i][j]);

    这样就将时间复杂度优化到O(n*m)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 100000 + 10;
    const int INF = 1e9 + 7;
    int n, m, k;
    char s[maxn];
    int Map[30][30];///Map[i][j]表示字母i变成字母j的代价
    int w[maxn][30];
    ///w[i][j]表示前i个都变成字母j的代价
    ///w[i][j] = w[i - 1][j] + Map[s[i - 1] - 'a' + 1][j]
    int dp[maxn][30];
    ///dp[i][j]表示在前i个中,第i个位置为字母j的最小代价
    ///此时要保证字符串是k连击 所以只存在以下两种情况
    ///1 i-1为字母j
    ///2 i-k+1 到 i 全都是字母j
    int Min[maxn];///Min[i]表示前i个最小代价
    
    int main()
    {
        scanf("%d%d%d", &n, &m, &k);
        scanf("%s", s);
        for (int i = 1; i <= m; i++)
            for (int j = 1; j <= m; j++)
                scanf("%d", &Map[i][j]);
        for (int k = 1; k <= m; k++)
            for (int i = 1; i <= m; i++)
                for (int j = 1; j <= m; j++)
                    Map[i][j] = min(Map[i][j], Map[i][k] + Map[k][j]);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                w[i][j] = w[i - 1][j] + Map[s[i - 1] - 'a' + 1][j];
        for (int i = 1; i <= min(n, 2 * k - 1); i++)///长度小于2*k-1 只能是同一个字母
        {
            Min[i] = INF;
            for (int j = 1; j <= m; j++)
                dp[i][j] = w[i][j], Min[i] = min(Min[i], dp[i][j]);
        }
        for (int i = 2 * k; i <= n; i++)
        {
            Min[i] = INF;
            for (int j = 1; j <= m; j++)
            {
                ///直接和上一位相同
                dp[i][j] = dp[i - 1][j] + Map[s[i - 1] - 'a' + 1][j];
                dp[i][j] = min(dp[i][j], Min[i - k] + w[i][j] - w[i - k][j]);///加上i-k+1到i都是j的代价
                Min[i] = min(Min[i], dp[i][j]);
            }
        }
        int ans = INF;
        for (int i = 1; i <= m; i++)
            ans = min(ans, dp[n][i]);
        cout << ans << endl;
        return 0;
    }
  • 相关阅读:
    封装TensorFlow神经网络
    android对话框显示异常报错:You need to use a Theme.AppCompat theme (or descendant) with this activity.
    管道过滤器模式
    架构设计模式之管道——过滤器模式
    SQL SERVER 数据库邮件配置
    浅谈数据仓库的基本架构(转)
    Spark On YARN内存分配
    Tomcat 9.0安装配置
    Spark on Yarn遇到的几个问题
    yarn资源调度(网络搜集)
  • 原文地址:https://www.cnblogs.com/ZJNU-huyh/p/13280894.html
Copyright © 2011-2022 走看看