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;

}

 

你可能感兴趣的:(动态规划)