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

posted @ 2013-04-30 21:24  yefeng1627  阅读(213)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor