• poj 3034 WhacaMole 动态规划,线段上点个数


       dp(t,x,y) 表示 t时刻,锤子放到 (x,y) 点,打地鼠最高分

       状态方程:

          dp(t, x, y) = Max { dp(t,x,y),dp(t-1,x1,y1)+ sum(x,y,x1,y1) } 

       其中  sqrt(  (x-x1)^2 + (y-y1)^2 ) <= d,     sum(x,y,x1,y1)表示 以 (x,y),(x1,y1)为端点的线段上 地鼠数量

      

       对于 已知两端点 (x1,y1),(x2,y2)的线段,求其线段上 合法点个数,有多种方式:

          一,  利用 直线方程来解,  若 x1==x2 或 y1 == y2  斜率不存在的特殊情况下,只需分别枚举 [y1,y2], [x1,x2]  (假定 y1 <= y2, x1 <= x2)

    否则,则根据直线的方程来解: 

           

    =>      

       若      则 y 有整数解,  我们可以枚举 x, [ x1,x2]

          二,  若 dx = | x1-x2 |, dy = | y1 - y2 | ,   d = Gcd(dx, dy)

              则 必有  

              x1 + K*(dx/d)  = X2, 

              y1 + K*(dy/d)  = Y2;      注意处理 dx , dy 都为0的情形

    解题代码

    View Code
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<vector>
    #include<math.h>
    using namespace std;
    #define MAX(a,b) (a)>(b)?(a):(b)
    #define MIN(a,b) (a)<(b)?(a):(b)
    int dp[11][50][50];
    int vis[50][50];
    int n, d, m;
    
    vector< pair<int,int> > Q[15];
    
    bool legal( int x, int y )
    {
        if( (x>=-5) && (x<n+5) && (y>=-5) && (y<n+5) )
            return true;
        return false;
    }
    int sum( int x1, int y1, int x2, int y2 )
    {
        int res = 0;    
        if( (x1 == x2) || (y1 == y2) )
        {
            if( x1 == x2 )
            {
                int s = MIN(y1,y2), t = MAX(y1,y2);     
                for(int y = s; y <= t; y++)    
                    res += vis[x1+5][y+5];    
                return res;
            }
            else
            {
                int s = MIN(x1,x2), t = MAX(x1,x2);
                for(int x = s; x <= t; x++)
                    res += vis[x+5][y1+5];
                return res;    
            }    
        }
        else
        {
            int s = MIN(x1,x2), t = MAX(x1,x2);    
            int ty = y1-y2, tx = x1-x2;
            for(int x = s; x <= t; x++)
            {
                int tmp = (x-x1)*ty;
                if( tmp%tx == 0 ){
                    int y = tmp/tx + y1;
                    res += vis[x+5][y+5];
                }
            }
            return res;    
        }
        return res;
    }
    
    // 增量求解两点间合法点数
    int gcd(int a,int b){ return b==0?a:gcd(b,a%b); }
    int getsum( int x1, int y1, int x2, int y2, int dx, int dy )
    {
        int res = vis[x1+5][y1+5];
        do
        {
            x1 += dx;
            y1 += dy;
            res += vis[x1+5][y1+5];    
        }while( !(x1==x2&&y1==y2) );
        return res;
    }
    int main()
    {
        while( scanf("%d%d%d", &n,&d,&m) != EOF)
        {
            if( n+d+m == 0 ) break;
            int x, y, t, maxt = 0;
            
            for(int i = 0; i <= 11; i++) Q[i].clear();
            for(int i = 0; i < m; i++)
            {
                scanf("%d%d%d",&x,&y,&t);
                Q[t].push_back( make_pair(x,y) );
                maxt = MAX( maxt, t );    
            }
            memset( vis, 0, sizeof(vis) );
            memset( dp, 0, sizeof(dp) );
        
            for( t = 1; t <= maxt; t++)
            {
                // 更新 t 时刻地鼠信息    
                memset( vis, 0, sizeof(vis) );
                for(int i = 0; i < Q[t].size(); i++)
                    vis[ Q[t][i].first+5 ][ Q[t][i].second+5 ] = 1;
        
                // dp(t,x,y) t时,锤子在(x,y)位置,最高得分 
                for(x = -5; x < n+5; x++)
                    for(y = -5; y < n+5; y++)
                    {
                        // 枚举可由t-1时刻(x1,y1)位置状态得到
                        for(int dx = -d; dx <= d; dx++)
                            for(int dy = -d; dy <= d; dy++)
                            {
                                int x1 = x+dx, y1 = y+dy;
                                if( (legal(x1,y1) == false) || ( (int)ceil( sqrt(dx*dx+dy*dy) ) > d )  ) 
                                    continue;
                                dp[t][x+5][y+5] = MAX(dp[t][x+5][y+5],dp[t-1][x1+5][y1+5]+sum(x,y,x1,y1) );    
                        /*  // 利用GCD增量法求解, 要特殊处理 公约数为0 的情况    
                                if( (dx==0) && (dy==0) ){
                                    dp[t][x+5][y+5] = MAX( dp[t][x+5][y+5] ,dp[t-1][x+5][y+5]+vis[x+5][y+5] );    
                                    continue;    
                                }    
                                int d = gcd( abs(dx),abs(dy));        
                                int tmp = getsum( x,y,x1,y1,dx/d,dy/d );    
                                dp[t][x+5][y+5] = MAX( dp[t][x+5][y+5], dp[t-1][x1+5][y1+5] + tmp );        
                        */    
                            }
                    }
            }
            int ans = 0;
            for(int i = -5; i < n+5; i++)
                for(int j = -5; j < n+5; j++)
                    ans = MAX( dp[maxt][i+5][j+5], ans );
            printf("%d\n", ans );    
        }
        return 0;
    }
  • 相关阅读:
    【ZOJ2112】【整体二分+树状数组】带修改区间第k大
    【POJ2104】【整体二分+树状数组】区间第k大
    【清澄A1333】【整体二分+二维树状数组】矩阵乘法(梁盾)
    【BZOJ2752】【线段树】高速公路
    【POJ2886】【线段树】Who Gets the Most Candies?
    【POJ2482】【线段树】Stars in Your Window
    【HDU4348】【主席树】To the moon
    JDBC
    java异常
    JavaScript对象
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/2860283.html
Copyright © 2020-2023  润新知