题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1605
题意:
平面直角坐标系中,有n个点,m个标记(坐标范围1~1000)。
你可以发出口令,让所有点整体向东、南、西、北四个方向中的任意一个方向移动,口令分别记作'E','S','W','N'。
每当一个点碰到一个标记,则答案+1。(保证初始时没有点在标记上)
你最多可以发出t次口令。
问你答案最大是多少,并输出字典序最小的口令序列。
题解:
表示状态:
dp[i][j][k] = max survivors
i:水平方向共移动了i个单位(左负右正)
j:竖直方向共移动了j个单位(下负上正)
k:发了k次口令
(因为数组下标有负数,所以最后再给i,j统一加上30就好啦,现在先不管它)
找出答案:
max dp[i][j][k]
如何转移:
先预处理cnt数组:cnt[x][y]表示移动了(x,y)时,碰到标记的点的个数。
dp[x][y][k] = cnt[x][y] + max dp[lx][ly][k-1]
lx=i-dx[p], ly=j-dy[p] (p = 0 to 3)
边界条件:
dp[0][0][0] = 0
others = -INF(不存在)
输出序列:
说明:
(1)feas[x][y][k],用来判断最终答案是否由状态(x,y,k)转移而来。
(2)way[x][y][k],表示在状态(x,y,k)时发出的口令。
总共三步:
(1)先将feas全部设为false,再将最终答案的feas改为true。
(2)枚举k(从大到小),i,j,判断当前(i,j,k)是否能转移到最终答案。如果能,更新当前way和feas。
(3)枚举k(从小到大),当前位置为(x,y),输出way[x][y][k],并更新(x,y)。
AC Code:
1 // state expression: 2 // dp[i][j][k] = max survivors 3 // i,j: moving dist 4 // k: whistling times 5 // 6 // find the answer: 7 // max dp[i][j][t] 8 // 9 // transferring: 10 // dp[x][y][k] = cnt[x][y] + max dp[lx][ly][k-1] 11 // 12 // boundary: 13 // dp[0][0][0] = 0 14 // others = -INF 15 // 16 // find the way: 17 // if dp[nx][ny][k+1] == cnt[nx][ny] + dp[x][y][k] and feas[nx][ny][k+1] 18 // way[x][y][k] = c[p] 19 // feas[x][y][k] = true 20 #include <iostream> 21 #include <stdio.h> 22 #include <string.h> 23 #include <stdlib.h> 24 #define MAX_N 1005 25 #define MAX_T 65 26 #define MAX_K 35 27 #define INF 10000000 28 29 using namespace std; 30 31 const int dx[]={1,0,0,-1}; 32 const int dy[]={0,1,-1,0}; 33 const char c[]={'E','N','S','W'}; 34 35 int n,m,t; 36 int ans=0; 37 int cx[MAX_N]; 38 int cy[MAX_N]; 39 int gx[MAX_N]; 40 int gy[MAX_N]; 41 int cnt[MAX_T][MAX_T]; 42 int dp[MAX_T][MAX_T][MAX_K]; 43 bool feas[MAX_T][MAX_T][MAX_K]; 44 char way[MAX_T][MAX_T][MAX_K]; 45 46 void read() 47 { 48 cin>>n>>m>>t; 49 for(int i=0;i<n;i++) 50 { 51 cin>>cx[i]>>cy[i]; 52 } 53 for(int i=0;i<m;i++) 54 { 55 cin>>gx[i]>>gy[i]; 56 } 57 } 58 59 void cal_cnt() 60 { 61 memset(cnt,0,sizeof(cnt)); 62 for(int i=0;i<n;i++) 63 { 64 for(int j=0;j<m;j++) 65 { 66 int x=gx[j]-cx[i]; 67 int y=gy[j]-cy[i]; 68 if(abs(x)+abs(y)<=t) cnt[x+30][y+30]++; 69 } 70 } 71 } 72 73 void cal_dp() 74 { 75 for(int k=0;k<=t;k++) 76 { 77 for(int i=0;i<=60;i++) 78 { 79 for(int j=0;j<=60;j++) 80 { 81 dp[i][j][k]=-INF; 82 } 83 } 84 } 85 dp[30][30][0]=0; 86 for(int k=1;k<=t;k++) 87 { 88 for(int i=0;i<=60;i++) 89 { 90 for(int j=0;j<=60;j++) 91 { 92 if(i+j-60>t) continue; 93 for(int p=0;p<4;p++) 94 { 95 int lx=i-dx[p]; 96 int ly=j-dy[p]; 97 dp[i][j][k]=max(dp[i][j][k],cnt[i][j]+dp[lx][ly][k-1]); 98 } 99 ans=max(ans,dp[i][j][k]); 100 } 101 } 102 } 103 } 104 105 void cal_way() 106 { 107 memset(feas,false,sizeof(feas)); 108 for(int i=0;i<=60;i++) 109 { 110 for(int j=0;j<=60;j++) 111 { 112 if(dp[i][j][t]==ans) feas[i][j][t]=true; 113 } 114 } 115 for(int k=t-1;k>=0;k--) 116 { 117 for(int i=0;i<=60;i++) 118 { 119 for(int j=0;j<=60;j++) 120 { 121 for(int p=0;p<4;p++) 122 { 123 int nx=i+dx[p]; 124 int ny=j+dy[p]; 125 if(dp[nx][ny][k+1]==cnt[nx][ny]+dp[i][j][k] && feas[nx][ny][k+1]) 126 { 127 way[i][j][k]=c[p]; 128 feas[i][j][k]=true; 129 break; 130 } 131 } 132 } 133 } 134 } 135 } 136 137 void solve() 138 { 139 cal_cnt(); 140 cal_dp(); 141 cal_way(); 142 } 143 144 void print() 145 { 146 cout<<ans<<endl; 147 int x=30; 148 int y=30; 149 for(int k=0;k<t;k++) 150 { 151 cout<<way[x][y][k]; 152 for(int p=0;p<4;p++) 153 { 154 if(way[x][y][k]==c[p]) 155 { 156 x+=dx[p]; 157 y+=dy[p]; 158 break; 159 } 160 } 161 } 162 } 163 164 int main() 165 { 166 read(); 167 solve(); 168 print(); 169 }