poj 3034 Whac-a-Mole 动态规划,线段上点个数

   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;
}

 

posted @ 2013-01-14 20:24  yefeng1627  阅读(232)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor