1. 程式人生 > >T - Whac-a-Mole POJ - 3034 (動態規劃)

T - Whac-a-Mole POJ - 3034 (動態規劃)

T - Whac-a-Mole

 POJ - 3034 

While visiting a traveling fun fair you suddenly have an urge to break the high score in the Whac-a-Mole game. The goal of the Whac-a-Mole game is to… well… whack moles. With a hammer. To make the job easier you have first consulted the fortune teller and now you know the exact appearance patterns of the moles.

The moles appear out of holes occupying the n2 integer points (xy) satisfying 0 ≤ xy < nin a two-dimensional coordinate system. At each time step, some moles will appear and then disappear again before the next time step. After the moles appear but before they disappear, you are able to move your hammer in a straight line to any position (x

2, y2) that is at distance at most d from your current position (x1, y1). For simplicity, we assume that you can only move your hammer to a point having integer coordinates. A mole is whacked if the center of the hole it appears out of is located on the line between (x1, y1) and (x2, y
2) (including the two endpoints). Every mole whacked earns you a point. When the game starts, before the first time step, you are able to place your hammer anywhere you see fit.

Input

The input consists of several test cases. Each test case starts with a line containing three integers nd and m, where n and d are as described above, and m is the total number of moles that will appear (1 ≤ n ≤ 20, 1 ≤ d ≤ 5, and 1 ≤ m ≤ 1000). Then follow m lines, each containing three integers xy and t giving the position and time of the appearance of a mole (0 ≤ xy < n and 1 ≤ t ≤ 10). No two moles will appear at the same place at the same time.

The input is ended with a test case where n = d = m = 0. This case should not be processed.

Output

For each test case output a single line containing a single integer, the maximum possible score achievable.

Sample Input

4 2 6
0 0 1
3 1 3
0 1 2
0 2 2
1 0 2
2 0 2
5 4 3
0 0 1
1 2 1
2 4 1
0 0 0

Sample Output

4
2

題意:第1秒前,錘子可以放在任何地方,假設為pos(x,y),那麼第一秒錘子移到距離pos(x,y)不遠於d的位置(相當於半徑),假設移到位置pos(x1,y1),那麼線段pos(x,y)--pos(x1,y1)上所有第一秒出現的老鼠都可以被擊中,擊中一個老鼠加1分,同樣的操作,到最後一秒,問最後可以得到的最大分值是多少

思路:用動態規劃思想,我們定義dp[i][j][k]為第k秒前,第k-1秒末到達位置pos(i,j)所得的最大分值,從第1秒開始到最後一秒,列舉第一秒可以從每一個位置出發的情況,可以得到一個狀態轉移方程

dp[i][j][k]=min(dp[i][j][k],dp[i][j][k-1]+ans),其中ans為第k秒中砸中的老鼠數(即得分)

這裡有一個地方是要優化的,即每次的移動距離不是1個單位1個單位的加,然後進行判斷,而是可以根據三角形的相似關係進行大距離加上;

舉個例子,假設從位置pos(x1,y1)到位置pos(x2,y2),兩點的距離為ds,rx=x2-x1=6,ry=y2-y1=8,那麼如果有一個點pos(x0,y0)在這兩點的連線上,由於這點的座標是整數,那麼只有符合(x0-x1)/(y0-x1)=(x2-x1)/(y2-y1) 的條件才行(三角形相似關係),轉一下思路,即只要求出rx,ry的最大公約數gcd,移動跨度rx=rx/gcd,ry=ry/gcd,然後從pos(x1,y1)出發,主要x1+rx<=x2且x2+ry<=y2,那麼此次移動所有在直線數的都不多不少的遍歷到,對於上面的例子,只有pos(x1,y1),pos(x1+3,y1+4),pos(x1+6,y1+8)在該線段上
 

#include<cstdio>
#include<stack>
#include<set>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<iostream>
#include<cmath>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
const int N=15;
const int nmax=44;
const double esp = 1e-9;
const double PI=3.1415926;
int dp[nmax][nmax][N],has[nmax][nmax][N];
struct node
{
    int x,y,ds;
} dir[nmax*nmax];
int n,m,d,sum;
bool cmp(node n1,node n2)
{
    return n1.ds<n2.ds;
}
void init()
{
    //預處理移動的距離資訊
    sum=0;
    memset(dir,0,sizeof(dir));
    for(int x1=-5; x1<=5; x1++)
        for(int y1=-5; y1<=5; y1++)
        {
            if(x1*x1+y1*y1<=25)
            {
                dir[sum].x=x1;
                dir[sum].y=y1;
                dir[sum++].ds=x1*x1+y1*y1;
            }
        }
    sort(dir,dir+sum,cmp);  
}
int gcd(int i,int j)  //最大公約數
{
    return j==0?i:gcd(j,i%j);
}
int solve(int x1,int y1,int x2,int y2,int dx,int dy,int tt)
{
    int ans=0;
    while(1)
    {
        ans+=has[x1][y1][tt];  
        if(x1==x2&&y1==y2)
            break;
        x1+=dx;
        y1+=dy;
    }
    return ans;
}
int main()
{
    init();
    while(scanf("%d%d%d",&n,&d,&m)!=EOF)
    {
        if(n==0&&d==0&&m==0)
            break;
        memset(dp,0,sizeof(dp));
        memset(has,0,sizeof(has));
        int xx,yy,t;
        int tmax=0;
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&xx,&yy,&t);
            has[xx+d][yy+d][t]=1;
            tmax=max(tmax,t);
        }
        int ans=0;
        tmax++;
        /***************核心程式碼**********************************/
        for(int k=1; k<tmax; k++)
        {
            for(int i=0; i<n+2*d; i++)
            {
                for(int j=0; j<n+2*d; j++)
                {
                    for(int ad=0; ad<sum&&d*d>=dir[ad].ds; ad++)
                    {   //遍歷經過的點
                        int dx=i+dir[ad].x;
                        int dy=j+dir[ad].y;
                        if(dx<0||dx>=n+2*d||dy<0||dy>=n+2*d)
                            continue; 
                        if(ad==0) //移動距離為0的情況下
                            ans=has[dx][dy][k];
                        else
                        {
                            int r=gcd(abs(dir[ad].x),abs(dir[ad].y));
                            int a=dir[ad].x/r;
                            int b=dir[ad].y/r;
                            ans=solve(i,j,dx,dy,a,b,k);
                        }
                        dp[dx][dy][k+1]=max(dp[dx][dy][k+1],dp[i][j][k]+ans);
                    }
                }
            }
        }
        /***************核心程式碼**********************************/
        int maxl=0;
        for(int i=d; i<d+n; i++)
            for(int j=d; j<d+n; j++)
                maxl=max(maxl,dp[i][j][tmax]);
        printf("%d\n",maxl);
    }
    return 0;
}