zoukankan      html  css  js  c++  java
  • NB 201351 赛后随笔

    A 题:  越来越弱,都掉渣了....连暴力递归模拟都不会写了...对每个黑棋递归搜索其边界,若遇到相同的黑棋则数量+1继续找,遇到0或者边界则直接return.

    View Code
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    
    char mp[110][110];
    bool flag;
    int n, m, dir[4][2] = {0,1,0,-1,1,0,-1,0};
    bool legal(int x,int y){
        if( x>=0&&x<n&&y>=0&&y<m)
            return true;
        return false;
    }
    int find(int x,int y){
        mp[x][y] = '1';    
        int s = 0;
        for(int i = 0; i < 4; i++){
            int xx = x+dir[i][0], yy = y+dir[i][1];
            if( legal(xx,yy) ){
                if( mp[xx][yy] == '0' ) flag = false;
                else if( mp[xx][yy] == '2' ) 
                    s += find(xx,yy);
            }
            else flag = false;    
        }
        if( flag ) return s+1;
        return 0;
    }
    int main(){
        while( scanf("%d%d",&n,&m) != EOF){
            for(int i = 0; i < n; i++)
                scanf("%s", mp[i] );
            int s = 0;        
            for(int i = 0; i < n; i++)
                for(int j = 0; j < m; j++){
                    if( mp[i][j] == '2' ){
                        flag = true;
                        s += find(i, j);
                    }    
                }
            printf("%d\n", s );    
        }
        return 0;
    }

    B题: 简单DP,一个状态dp(i,j) 由左右下,三个方向传递而来,所以dp(i,j) = min( dp(i,j-1), dp(i,j+1), dp(i-1,j) ) + cost(i,j)

    View Code
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    using namespace std;
    
    int n, m;
    int dp[25][25];
    int val[25][25];
    
    int main(){
        while( scanf("%d%d", &n,&m) != EOF){
            memset( dp, 0x3f, sizeof(dp));
            memset( val, 0x3f, sizeof(val));        
            for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)    
                scanf("%d", &val[i][j] );    
            for(int i = 1; i <= m; i++ )    
                dp[n][i] = val[n][i];
            for(int i = n-1; i >= 1; i--){
                for(int j = 1; j <= m; j++)    
                    dp[i][j] = dp[i+1][j] + val[i][j];
                for(int j = 2; j <= m; j++)
                    dp[i][j] = min( dp[i][j], dp[i][j-1]+val[i][j] );
                for(int j = m-1; j >= 1; j--)
                    dp[i][j] = min( dp[i][j], dp[i][j+1]+val[i][j] );
            }
            int res = 0x3f3f3f3f;
            for(int i = 1; i <= m; i++)
                res = min(res, dp[1][i] );
            printf("%d\n", res );    
        }
        return 0;
    }

    C题: 最大子矩阵和, 首先枚举起始列(1,n), 然后枚举长度, 计算长度的时候从小到大,这样就可以利用前面求得的和.然后再将每一行看作一个点,

    求一维的最大连续和. 总时间复杂度为 O(N^3) 

    View Code
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    const int inf = 0x3fffffff;
    
    int a[110][110], b[110];
    int dp[110], ans, n;
    
    int main()
    {
        while( scanf("%d", &n) != EOF)
        {
            for(int i = 1; i <= n; i++)
                for(int j = 1; j <= n; j++)
                    scanf("%d", &a[i][j]);
            ans = -inf;    
            for(int i = 1; i <= n; i++)
            {
                memset( b, 0, sizeof(b));    
                for(int j = i; j <= n; j++)
                {
                    for(int k = 1; k <= n; k++)
                        b[k] += a[j][k];
                    int tmp = -inf;
                    for(int k = 1; k <= n; k++)
                    {
                        if( tmp < 0 ) tmp = b[k];
                        else    tmp += b[k];
                        if(tmp > ans) ans = tmp;
                    }
                }
            }    
            printf("%d\n", ans );    
        }
        return 0;
    }

    D题: 贪心, 对于连续不为0长度大于等于三的 一列题目, 我们知道做其中一个i, 并使其 cost(i-1) + cost(i) + cost(i+1) 尽可能大,总是最优. 基于此的贪心即可.

    View Code
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    
    struct node{
        int x, c, idx;
    }t;
    
    int a[110], n;
    int main(){
        while( scanf("%d",&n) != EOF){
            for(int i = 0; i < n; i++){
                scanf("%d", &a[i] );    
            }    
            int idx = -1, res = 0;    
            for(int i = 0; i < n; i++)    
                if( a[i] ) { idx = i; break; }    
            while( idx != -1 ){
                int l = idx, r = l;
                while( (r+1) < n && a[r+1] ) r++;
                //printf("l = %d, r = %d\n", l, r );    
                //for(int i = 0; i < n; i++) printf("%d ",a[i]); puts("");    
                if( r-l+1 <= 2 ){
                    res += max( a[l], a[r] );    
                    a[l] = a[r] = 0;
                }    
                else{    
                    bool find = false;    
                    for(int i = l+1; i <= r-1; i++){
                        if( find == false ){
                            find = true;
                            t.idx = i; t.x = a[i]; 
                            t.c = min(a[i-1],a[i])+a[i]+min(a[i+1],a[i]);
                            if( 3*t.x == t.c ) break;    
                        }    
                        else{
                            int x = a[i], c = min(a[i-1],a[i])+a[i]+min(a[i+1],a[i]);
                            if( 3*x == c ){ 
                                t.idx = i; t.c = c; t.x = x; 
                                break;
                            }    
                            if( (c>t.c)||((c==t.c)&&(x<t.x))){
                                t.idx = i; t.c = c; t.x = x;    
                            }    
                        }    
                    }        
                    res += t.x;
                    int pos = t.idx;// printf("pos = %d\n", pos );
                    a[ pos-1 ] -= min( a[pos-1],t.x );
                    a[ pos ]   -= t.x;
                    a[ pos+1 ] -= min( a[pos+1],t.x );
                }    
                idx = -1;    
                for(int i = 0; i < n; i++) if(a[i]){idx=i;break;}    
                    
            }    
            printf("%d\n", res );
        }    
        return 0;
    }

    E题: 简单计算集合, 可是我老WA,各种无奈. 解法是这样的.  若我们假定两个矩形 A,B ,其中A在左边, B在右边. (若给的A在右,我们替换AB即可)

    这时候不同的情况有两种:

        一: A 与 B 的区间相交 

          x区间相交, 则A在下B在上时,距离为( B.y1 - A.y2 ), 否则( A.y1 - B.y2 )

          y区间相交, 则因为A必定是在左侧,则距离为 (B.x1 - A.x2)

        二: A 与 B 的区间不相交

          若A在下,B在上, 则距离为 dist(  A(x2,y2), b(x1,y1) ),否则 dist( A(x2,y1), B(x1,y2) )

    View Code
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    const double eps = 1e-8;
    struct node{
        double x1,y1,x2,y2;
        void input(){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
        }
    }p1,p2;
    
    int sign( double x ){
        return x<-eps ? -1 : (x>eps);
    }
    bool legal( double a,double b, double x1, double x2 ){
        // x1 <= a <= x2 || x1 <= b <= x2    
        if( (sign(a-x1)>=0 && sign(x2-a)>=0)
                || (sign(b-x1)>=0 && sign(x2-b)>=0) ) return true;
        return false;
    }
    double dist(double x1,double y1,double x2,double y2){
        return sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) );
    }
    double solve(){
        if( sign(p1.x1-p2.x1) >= 0 ) swap( p1, p2 );    
        if( legal(p1.y1,p1.y2,p2.y1,p2.y2) || legal(p2.y1,p2.y2,p1.y1,p1.y2))
            return abs(p2.x1 - p1.x2);    
        if( legal(p1.x1,p1.x2,p2.x1,p2.x2) ||legal(p2.x1,p2.x2,p1.x1,p1.x2) ){
            if( sign(p2.y1-p1.y1)>=0 ) return abs(p2.y1 - p1.y2);
            else return abs(p1.y1 - p2.y2);    
        }    
        if( sign(p2.y1-p1.y1) >= 0 ) return dist( p1.x2, p1.y2, p2.x1, p2.y1 );
        else return dist( p1.x2, p1.y1, p2.x1, p2.y2 );
    }
    int main(){
        int T;
        scanf("%d", &T);
        while( T-- ){
            p1.input(); p2.input();
            double ans = solve();    
            printf("%.4f\n", ans );
        }
        return 0;
    }

    F题:  感谢出题人的点拨.学会了这道题. 再次Orz一下出题人...

      我们观察这样 n = 2, k = 7 的情况来说明题目解法:

           11          --1      //0个0

         101     --2      // 1个0

         110     --3

       1001     --4      // 2个0        

       1010     --5 

       1100     --6

      10001    --7       //3个0

      10010    --8

      10100    --9

      11000  --10

    假定0的数量为 r, 因为1的数量n, 则长度为 r+n的这段排列的方案有,  \binom{ n+r-1 }{ r-1 } , (1之间的空隙,看作有区别盒子,0看作无区别球.方案数为\binom{n+r-1}{r-1})

    从1 开始到第k大, 我们要找到第一个满足  s = \sum \binom{n+r-1}{r-1}  > k , 的这个s, 其中r为0的数量.

    当找到第一个大于k的s后, 当前长度最小的形式为  01...10..0 , 其中前面 n个1, 后面r-1个0. 然后进行全排列.

    View Code
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    int p[1010];
    
    int Binom( int a, int b ){
        int s = 1;
        for(int i = 0; i < a; i++)
            s = s*(b-a+1+i)/(i+1);
        return s;
    }
    int main(){
        int n, k, T; scanf("%d", &T);
        while( T-- ){
            scanf("%d%d", &k,&n);    
            if( n == 1 ){
                printf("1");    
                for(int i = 0; i < k-1; i++)printf("0");puts("");    
                continue;
            }    
            int s = 0, r = 0;
            while(1){
                int t = Binom( n-1, n+r-1 );
                if( s+t > k ) break; // 1000...01..1 format
                s += t; r++;    
            }
            p[0] = 0;    
            for(int i = 1; i <= n; i++) p[i] = 1;
            for(int i = n+1; i < n+r; i++) p[i] = 0;
            r = n+r;
            while( s < k ) s++,next_permutation( p, p+r ); 
            if(p[0]) printf("%d",p[0]);     
            for(int i = 1; i < r; i++) printf("%d",p[i]);puts("");    
        }
        return 0;
    }

    X题:  神题..看都没看...

    Y题: 

      对于这题,我们假设只有两个含N个元素的数组, A,B, 求从两数组中各取一个相加,求其前N小.,假定A,B数组有序,根据单调性有:

      A1+B1 <= A1+B2 <= A1+B3 <= ... <= A1+Bn

      A2+B1 <= A2+B2 <= A2+B3 <= ... <= A2+Bn

      ...

      An+B1 <= An+B2 <= An+B3 <= ... <= An+Bn

      对于 Ai+Bj而言, 从其位置(i,j)出发(单一变量原则) 下一个(向右,向下)最小值,只可能是(AI+1, Bj) 或(Ai,Bj+1). 

      那么,我们首先把 A1+B1 , A1+B2 , ... ,A1+Bn放到堆中, 将所有右侧最小值先放入,这样每次取出最小元素A(x,y)时,只需要

    将其下方元素再加入堆即可,A(x+1,y), 因为若 已经取到A(x,y), 则A(x,1) - A(x,y-1) 必定都已被取. 而A(x-1,y+1)若还未取,而取A(x,y+1)

    不符合, 因为 A(x-1,y+1) <= A(x,y+1) ,由此可知此时将 A(x+1,y)放入堆中即可. 维护堆中一直只有N个元素,总时间复杂度为 NlogN.

      再回到本题, 因为总共N行, 每行N个元素, 那么我们将N行 二分归并下去, 划分成 2行,1行, 形式,组合之后再合并.即可.

      时间复杂度为 N(logN)^2

    View Code
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    using namespace std;
    
    const int N = 500;
    int val[N][N], n;
    
    struct node{
        int x, y, c;
        bool operator <(node tmp)const{
            return c > tmp.c;    
        }
    }t;
    int *cmp( int *A, int *B ){
        int *res = new int[n];
        priority_queue<node>Q;
        while( !Q.empty() ) Q.pop();    
        for(int i = 0; i < n; i++){
            t.x = 0, t.y = i;
            t.c = A[0] + B[i];
            Q.push(t);    
        }
        for(int i = 0; i < n; i++){
            t = Q.top(); Q.pop();
            res[i] = t.c;
            t.x++; t.c = A[t.x] + B[t.y];
            Q.push(t);
        }
        return res;
    }
    int *solve(int l,int r){
        int *res = new int[n];
        if( l == r ) return val[l];    
        if( r-l+1 == 2 ) res = cmp( val[l], val[r] );    
        else{
            int m = (l+r)>>1;
            int *r1 = solve( l, m ), *r2 = solve( m+1, r );
            res = cmp( r1, r2 );
        }    
        return res;
    }
    int main(){
        while( scanf("%d", &n) != EOF){
            for(int i = 0; i < n; i++){
                for(int j = 0; j < n; j++)
                    scanf("%d", &val[i][j] );    
                    sort( val[i], val[i]+n );                
                }    
            int *res = solve(0,n-1);
            for(int i = 0; i < n; i++)
                printf(i==0?"%d":" %d",res[i]);
            puts("");
        }        
        return 0;
    }

    Z题:  没写出来.. 

      想到了一个O(N^2)的DP,  先按(x,y)从小到大排序, 然后 dp(i) = max{ dp(k-1) + cost( k, i ) }  ..TLE.

      然后又想了个线性的贪心, 排序, 枚举起点i,  找个最大的j, 满足 max( Wi...Wj ) * max( Hi,..,Hj) >= Sum( Load_i, Load_j ), 

    则此段 Load( i, j ) 必定连起来买更划算.  WA了... 坐等解题报告.

  • 相关阅读:
    Influx Sql系列教程一:database 数据库
    Influx Sql系列教程零:安装及influx-cli使用姿势介绍
    移动端/H5关于cursor:pointer导致的问题
    onselectstart="return false"
    js正则验证之不能使用相同字符
    js通过sessionStorage实现的返回上一页
    MetaHandler.js:移动端适配各种屏幕
    iOS下的 Fixed + Input 调用键盘的时候fixed无效问题解决方案
    js判断三个数字中的最大值
    js判断微信浏览器
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/3052431.html
Copyright © 2011-2022 走看看