zoukankan      html  css  js  c++  java
  • 5月19日省中提高组题解

    【Problem A】 Square

    【题意】

              给一个n * n的01矩阵,要求一个最大的全1正方形子矩阵,输出它的面积

              N <= 1000

     【题解】

              朴素的做法是先求二维前缀和,然后暴力找最大的正方形子矩阵,时间复杂度 : O(n^3) 期望得分 : 80

              考虑优化,我们发现如果有边长为n的正方形,就一定有边长为(n - 1)的正方形,因此,可以先二分边长d,然后

              判断边长为d的正方形是否存在. 时间复杂度 : O(n^2log(n)) 期望得分 : 100

    【代码】

              

    #include<bits/stdc++.h>
    using namespace std;
    #define MAXN 2050
    
    int i,j,l,r,mid,n,ans;
    int s[MAXN][MAXN];
    char ch;
    
    bool check(int d)
    {
            int i,j,x,y;
            for (i = 1; i <= n - d + 1; i++)
            {
                    for (j = 1; j <= n - d + 1; j++)
                    {
                            x = i + d - 1;
                            y = j + d - 1;
                            if (s[x][y] - s[i-1][y] - s[x][j-1] + s[i-1][j-1] == d * d)
                                    return true;    
                    }        
            }        
            return false;
    }
    
    int main() {
            
            freopen("square.in","r",stdin);
            freopen("square.out","w",stdout);
            
            scanf("%d",&n);
            getchar();
            for (i = 1; i <= n; i++)
            {
                    for (j = 1; j <= n; j++)
                    {
                            ch = getchar();
                            s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + ch - '0';
                    }
                    getchar();
            }
            l = 1; r = n;
            while (l <= r)
            {
                    mid = (l + r) >> 1;
                    if (check(mid))
                    {
                            l = mid + 1;
                            ans = mid;
                    } else r = mid - 1;
            }
            printf("%d
    ",ans*ans);
            
            return 0;
        
    }

    【Problem B】 findmax

     【题意】

               一个包含n个元素的数组,元素的值在1-k之间,存在多少个不同的数组,使得扫描这个数组,最大值更新了p次

      【题解】

                 暴力搜索,期望得分 : 30

                 由于T最大10^5,所以,我们不可能每次询问都计算一次答案,因此,我们应该进行一些预处理

                 考虑动态规划预处理

                 f[i][j][k]表示选了i个元素,元素的最大值为j,进行了k次更新的合法方案

                 那么,有f[i][j][k] = f[i-1][j][k] * j + f[i-1][t][k-1] (1 <= t <= j - 1) 

                 这个状态转移方程的意思是 :

                如果选到第i个元素时最大值未发生变化,那么,就相当于前i-1个元素最大值为 j,进行了k次更新,而第i个元素就可                  以选1..j中的任何一个数,方案数为f[i-1][j][k] * j

                如果选到第i个元素时最大值发生了变化,那么,前i-1个元素,最大值可以是1..j-1中的任何一个数,进行了k-1次更                    新, 第i个元素为j,方案数为f[i-1][t][k-1] (1 <= t <= j - 1)

                然而,这样算法的时间复杂度是O(nk^2p),不能通过此题

                考虑优化,我们用sum[i][j][k]表示选了i-1个元素,进行j次更新,元素的最大值为1..k的方案数(前缀和),

                式子就被简化为了 : f[i][j][k] = f[i-1][j][k] * j + sum[i-1][k-1][j-1]

                那么,这样时间复杂度就被优化为了O(nkp)

                对于每次询问,答案就是sigma(f[n][i][p])(1 <= i <= k)

                总时间复杂度 : O(nkp + tk) 期望得分 : 100

       【代码】

                

    #include<bits/stdc++.h>
    using namespace std;
    #define MAXN 101
    #define MAXK 301
    #define MAXP 101
    const long long MOD = 1e9 + 7;
    
    long long i,j,k,ans,n,p,T;
    long long f[MAXN+10][MAXK+10][MAXP+10],sum[MAXN+10][MAXP+10][MAXK+10];
    
    int main() {
        
        freopen("findmax.in","r",stdin);
        freopen("findmax.out","w",stdout);
        
        cin >> T;
        
        for (i = 1; i <= MAXK; i++) 
        {
            f[1][i][0] = 1;
            sum[1][0][i] = i;
        }
        for (i = 2; i <= MAXN; i++)
        {
            for (j = 1; j <= MAXK; j++)
            {
                for (k = 0; k <= MAXP; k++)
                {
                    if (!k) f[i][j][k] = (f[i-1][j][k] * j) % MOD;
                    else f[i][j][k] = ((f[i-1][j][k] * j) % MOD + sum[i-1][k-1][j-1]) % MOD;
                    sum[i][k][j] = (sum[i][k][j] + f[i][j][k]) % MOD;
                }
            }
            for (j = 0; j <= MAXP; j++)
            {
                for (k = 1; k <= MAXK; k++)
                {
                    sum[i][j][k] = (sum[i][j][k] + sum[i][j][k-1]) % MOD; 
                }
            }
        }
        
        while (T--)
        {
            ans = 0;
            cin>> n >> k >> p;
            for (i = 1; i <= k; i++) ans = (ans + f[n][i][p]) % MOD;
            cout<< ans << endl;
        }
        
        return 0;
    }

    【Problem C】bds

     【题意】

                写出1..n的排列a,每次相邻两个数相加,构成新的序列,再对新序列再次进行这样的操作,知道剩下一个数N

                给定N,要求字典序排列最小的a

    【题解】

              调用algorithm库里的next_permutation函数,判断是否可以得到N 期望得分 : 20

              我们发现,N = C(n,1)a1 + C(n,2)a2 + C(n,3)a3 + C(n,4)a4 + ... + C(n,n)an

              因此,可以通过杨辉三角计算C(n,i),然后深度优先搜索DFS

              时间复杂度 O (能过) 期望得分 : 100

    【代码】

                

    #include<bits/stdc++.h>
    using namespace std;
    #define MAXN 25
    
    int i,j,n,sum,l;
    int c[MAXN][MAXN],visited[MAXN],ans[MAXN];
    bool solved = false;
    
    inline void dfs(int dep,int s)
    {
        int i;
        if (dep > n)
        {
            if (s == sum) 
                solved = true;
            return;
        }    
        for (i = 1; i <= n; i++)
        {
            if (s + i * c[n][dep] > sum) return;
            if (!visited[i])
            {
                visited[i] = true;
                ans[++l] = i;
                dfs(dep+1,s+i*c[n][dep]);
                if (solved) return;
                l--;
                visited[i] = false;
            }
        }
    }
        
    int main(){
        
        freopen("bds.in","r",stdin);
        freopen("bds.out","w",stdout);
        
        scanf("%d%d",&n,&sum);
            
        c[0][0] = 1;
        for (i = 1; i <= n; i++)
        {
            for (j = 1; j <= i; j++)
            {
                c[i][j] = c[i-1][j] + c[i-1][j-1];    
            }    
        } 
        
        dfs(1,0);
        
        for (i = 1; i < n; i++) printf("%d ",ans[i]);
        printf("%d
    ",ans[n]);
        
        return 0;
    }

    【总结】

              总分20 + 100 + 100 = 220,第一题失分原因是因为将正方形看成了长方形,这样的错误很不应该,以后

              一定要避免!

              这次考试,基础算法占了很大比重,虽然最近一直在研究比较复杂的算法,但是,也不能忽视了基础算法的

              重要性!

  • 相关阅读:
    Java 线程之间的通讯,等待唤醒机制
    Java 死锁以及死锁的产生
    2018Java开发面经(持续更新)
    OpenFlow1.3协议wireshark抓包分析
    SDN核心技术剖析和实战指南---读书笔记
    Kafka常用命令
    安装kafka+zk-ui
    flink窗口
    flink架构原理
    安装Flink集群
  • 原文地址:https://www.cnblogs.com/evenbao/p/9196310.html
Copyright © 2011-2022 走看看