zoukankan      html  css  js  c++  java
  • 2019牛客多校训练营第二场补题

    https://ac.nowcoder.com/acm/contest/882/F

    F题 Partition Problem

    题意:有两个队,n 个人,每个人和其他 n-1 有个竞技值,当处于不同队时,就能增加这个值,求最大的总竞技值。

    分析:因为 N 最大只有14,所以可以暴力DFS回溯搞组合数。

    对于这个问题,就得先构造一个初始状态入手,初始状态也就是全都在B队,由于关键在于对立,所以可以让1先在A队来简化,接下来的入A队就加上该加的竞技值,减去该减的竞技值。

    然后每个都用 for 不断往下搜索,记录vis[ ] 和 cnt,如果cnt 满了就比较结果,并退一步把当前位扔掉,然后继续往下搜索(八皇后)。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int maxn = 30;
    int val[maxn][maxn];
    bool in[maxn];
    int n,m;
    ll ans=0;
    
    void dfs(int now,int cnt,ll sum){
        if(cnt==n){
            ans = max(ans,sum);
            return;
        }
        for(int i=now; i<=n+cnt+1; i++){    //这里写n+cnt+1比写m快
            if(!in[i]){
                ll tmp=sum;
                for(int j=1; j<=m; j++){
                    if(in[j])
                        sum -= val[i][j];   //这里是i 而我一开始错写成了+=val[now][j],WA了好久
                    else
                        sum += val[i][j];
                }
                in[i] = true; 
                dfs(i+1,cnt+1,sum);
                in[i] = false;        //回溯
                sum = tmp;
            }
        }
    }
    
    inline int read(){
        int res = 0, w = 0; char ch = 0;
        while(!isdigit(ch)){
            w |= ch == '-', ch = getchar();
        }
        while(isdigit(ch)){
            res = (res << 3) + (res << 1) + (ch ^ 48);
            ch = getchar();
        }
        return w ? -res : res;
    }
    
    int main(){
        scanf("%d",&n);
        m = n<<1;
        for(int i=1; i<=m; i++){
            for(int j=1; j<=m; j++){
                val[i][j] = read();
            }
        }
        ll sum;
        sum = ans = 0;
        in[1] = true;   //先构造个初始状态,所有人都在红队,再把1放进白队
        for(int i=2; i<=m; i++){
            sum += val[1][i];
        }
        dfs(2,1,sum);
        printf("%lld
    ", ans);
        return 0;
    }
    View Code

    https://ac.nowcoder.com/acm/contest/882/H

    H题 Second Large Ractangle

    悬线法求极大子矩阵的裸题。 相关资料:https://www.cnblogs.com/-Zzz-/p/11503639.html给由01组成的矩阵,问只包含1的子矩阵第二大的面积是多少。

    由于这个预处理方法会导致一整块的L,R,U的值都同化,所以还要处理出这一块的(L-1)*H 和 L*(H-1)。

    还要注意 g[i][j] 等于 0 时不算数,不要拿去算面积

    更新ans1时要用大于等于号

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e3+300;
    int g[maxn][maxn],Left[maxn][maxn],Right[maxn][maxn],Up[maxn][maxn];
    char ch[maxn];
    
    int main(){
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; i++){
            scanf("%s",ch+1);
            for(int j=1; j<=m; j++){
                g[i][j] = ch[j]-'0';
                Up[i][j] = 1;
                Left[i][j] = Right[i][j] = j;
            }
        }
        for(int i=1; i<=n; i++){
            for(int j=2; j<=m; j++){
                if(g[i][j]==1 && g[i][j-1]==1){
                    Left[i][j] = Left[i][j-1];
                }
            }
        }
        for(int i=1; i<=n; i++){
            for(int j=m-1; j>=1; j--){
                if(g[i][j]==1 && g[i][j+1]==1){
                    Right[i][j] = Right[i][j+1];
                }
            }
        }
        int maxh = 0,maxl = 0;
        int L,R,U,ans1=0,ans2=0;
        for(int i=1; i<=n; i++){
            for(int j=1; j<=m; j++){
                if(i>1 && g[i][j]==1 && g[i-1][j]==1){
                    Up[i][j] = Up[i-1][j]+1;
                    Left[i][j] = max(Left[i-1][j], Left[i][j]);
                    Right[i][j] = min(Right[i-1][j], Right[i][j]);  //悬线法
                }
                if(ans1<= (Right[i][j] - Left[i][j] + 1)*Up[i][j] ){
                    R = Right[i][j], L = Left[i][j], U = Up[i][j];
                    maxl = Right[i][j] - Left[i][j] + 1;
                    // printf("l=%d
    ", maxl);
                    maxh = Up[i][j];
                    // printf("h=%d
    ", maxh);
                    ans1 = maxl*maxh;
    
                }
            }
        }
        ans2 = max(ans2,(maxl-1)*maxh);
        ans2 = max(ans2,maxl*(maxh-1));
    
        for(int i=1; i<=n; i++){
            for(int j=1; j<=m; j++){
                if(g[i][j]==0) continue;
                if(Right[i][j] == R && Left[i][j]==L && Up[i][j]==U) continue;
                ans2 = max( ans2,(Right[i][j] - Left[i][j] + 1)*Up[i][j] );
            }
        }
        printf("%d
    ", ans2);
    }
    
    
    // #include <iostream>
    // #include <algorithm>
    // using namespace std;
     
    // const int Max = 2005;
     
    // int ves[Max][Max], up[Max][Max], Left[Max][Max], Right[Max][Max];
    // int temp1 = 1, temp2 = 1;
     
    // int main(void)
    // {
    //     ios::sync_with_stdio(false);
    //     int N, M;
    //     cin >> N >> M;
    //     for(int i = 1; i <= N; i++)
    //         for (int j = 1; j <= M; j++) {
    //             cin >> ves[i][j];
    //             Left[i][j] = Right[i][j] = j;    //初始化Right和Left,使他们值为点所在纵坐标
    //             up[i][j] = 1;    //初始化up使其值为1
    //         }
     
    //     for (int i = 1; i <= N; i++)
    //         for (int j = 2; j <= M; j++)
    //             if (ves[i][j] == 1 - ves[i][j - 1])    //判断相邻两个数是否不同
    //                 Left[i][j] = Left[i][j - 1];    //是,则
     
    //     for (int i = 1; i <= N; i++)
    //         for (int j = M - 1; j > 0; j--)
    //             if (ves[i][j] == 1 - ves[i][j + 1])
    //                 Right[i][j] = Right[i][j + 1];
     
    //     for(int i = 1;i <= N; i++)
    //         for (int j = 1; j <= M; j++) {
    //             if (i > 1 && ves[i][j] == 1 - ves[i - 1][j]) {    //递推公式
    //                 Left[i][j] = max(Left[i][j], Left[i - 1][j]);
    //                 Right[i][j] = min(Right[i][j], Right[i - 1][j]);
    //                 up[i][j] = up[i - 1][j] + 1;
    //             }
     
    //             int A_instance = Right[i][j] - Left[i][j] + 1;    //计算长度
    //             int B_instance = min(A_instance, up[i][j]);    //算出长宽中较小的边,以计算正方形
    //             temp1 = max(temp1, B_instance * B_instance);    //正方形面积
    //             temp2 = max(temp2, A_instance * up[i][j]);        //长方形面积
    //         }
     
    //     cout << temp1 << endl << temp2 << endl;
    // }
    View Code

    A题 Eddy Walker 随机程序找规律和逆元

    参考自https://blog.csdn.net/WHY995987477/article/details/97494292

    题意:有n个点的环,初始位置在0,可以随机向前走或向后走,问n个位置都走过,并且最后停在m的概率,最后输出前i种情况的概率。

    分析:随机数暴力打表找规律,会发现概率是一样的,都等于1/(n - 1)。以及用到求单个逆元, a^(p2) 就是 a 在 mod p 意义下的逆元

    打表的时候n的值不要输入太大!

    要分组输入,不要多组输入

    另外有两个特殊情况:

    1、人一开始站在0上,0已经视为走过了。所以当n == 1 && m == 0 时,开始即终止,概率为1。

    2、而当n > 1 && m == 0时,因为走完n个点就结束了,从0点开始走,不可能刚好走完n个点后停在0处,此时的概率是0。

    最后要求输出的是每次的结果(概率)的累乘,具体看代码里面的ans。

    这里有一个不是用打表找规律做出来的博客:
    https://blog.nowcoder.net/n/95f1fe2b38aa4befaba3750128a12786
    随机数打表代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5 + 5;
    int vis[maxn], ans[maxn];
    int n;//一共有n个点
    int pos;//当前所在位置
    int cnt;//已经经过了几个点。
    int main()
    {
        scanf("%d", &n);//不要用多组样例输入,不要输入太大的n值
        srand((unsigned int)(time(NULL)));
        for(int i = 1; i <= 100000; i++)
        {
            memset(vis, 0, sizeof(vis));
            vis[0] = 1;
            pos = 0;
            cnt = 1;
            while(cnt < n)
            {
                int moving = rand() % 2 ? 1 : -1;
                pos = (pos + moving + n) % n;
                if(!vis[pos])
                {
                    vis[pos] = 1;
                    cnt++;
                }
                if(cnt == n)
                {
                    ans[pos]++;
                }
            }
        }
        for(int i = 0;i < n; i++)
            printf("%d ", ans[i]);
        printf("
    ");
        return 0;
    }
    View Code

    AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int mod = 1e9+7;
    
    ll qpow(int x,int y){
        ll res = 1;
        while(y){
            if(y&1) res= res%mod *x%mod;
            y>>=1;
            x = (ll)x%mod*(ll)x%mod;
        }
        return res;
    }
    
    int main(){
        int T,n,m;
        scanf("%d",&T);
        ll res,ans=1;
        while(T--){
            scanf("%d%d",&n,&m);
            if(n==1 && m==0) res=1;
            else if(m==0) res=0;
            else{
                res = 1*qpow(n-1,mod-2);
            }
            ans = ans%mod * res%mod;
            printf("%lld
    ", ans);
        }
    }
    View Code

    D题 Kth Minimum Clique 优先队列优化的bfs 

    参考自:https://www.cnblogs.com/Chen-Jr/p/11222852.html#autoid-1-0-0

    题意:让你求出一张图的第k小团的权值。

    分析:

    注意,要防止重复,就是每次找最后一位的下一位开始枚举。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int maxn = 102;
    bitset<maxn> a[maxn];
    struct node{
        ll a;
        bitset<maxn> b;
        bool operator < (const node & t)const{
            return a > t.a;
        }
    };
    priority_queue<node> que;
    int w[maxn];
    char ch[maxn];
    
    int main(){
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i=0; i<n; i++){
            scanf("%d", w+i);
        }
        for(int i=0; i<n; i++){
            scanf("%s",ch);
            for(int j=0; j<n; j++){
                if(ch[j] == '1'){
                    a[i].set(j);
                }
            }
        }
        bitset<maxn> t; t.reset();
        que.push( (node){0,t} );
        while(!que.empty()){
            node u = que.top();
            bitset<maxn> tmp = u.b;
            que.pop();
            k--;
            if(k==0){
                printf("%lld
    ", u.a);
                return 0;
            }
            int pos = 0;
            for(int i=0; i<n; i++){
                if(tmp[i]) pos = i+1;    ///为了保证不会重复加点,我们每次从一个已经加入的点的后一个结点开始枚举新加入的结点
            }    
            for(int i=pos; i<n; i++){
                if( !tmp[i] && (tmp&a[i])==tmp){
                    tmp[i] = 1;
                    que.push( (node){u.a + w[i] ,tmp} );
                    tmp[i] = 0;   //记得这个小回溯
                }
            }
        }
        puts("-1");
    
    }
    View Code

     

  • 相关阅读:
    [收藏转贴]struct探索·extern "C"含义探索 ·C++与C的混合编程·C 语言高效编程的几招
    [收藏转贴]构建RESTful风格的WCF服务
    [转贴] C/C++中动态链接库的创建和调用
    [转贴]WebService的简单实现 C++
    谷歌已经对Android的开源严防死守
    如何成为一名黑客?
    当程序员思路被打断
    编辑语言是这样的
    开发者需要掌握多少门语言?
    程序员的六大优势
  • 原文地址:https://www.cnblogs.com/-Zzz-/p/11504077.html
Copyright © 2011-2022 走看看