NB 2013-5-1 赛后随笔

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了... 坐等解题报告.

你可能感兴趣的:(随笔)