BZOJ1605: [Usaco2008 Open]Crisis on the Farm 牧场危机

n<=1000个牛塔,每塔30头牛,m<=1000个地点会使经过的牛塔少一头,K<=30个命令每次使所有牛塔往东西南北某方走一格,求最多损失多少牛并输出字典序最小的方案。

没看到K的范围想不出系列。。由于K<=30,不会出现牛负数的情况,所以直接搜,搜可能会搜到重复,那就记忆化,那不如直接写DP。

f(k,i,j)--走了k步,所有牛相对原来横坐标走了i纵坐标走了j,f(k,i,j)=max f(k-1,四个方向) +s(i,j),其中s(i,j)表示所有牛往右走i格往上走j格(i,j可以是负数)损失的牛,这可以预处理。

最后的方案要字典序最小。由于某个最优状态的i,j是一定的,也就是W-E的数量和N-S的数量是一定的,那后面的W越多,前面的E就越多,字典序就越小,而DP记状态是从后往前走的,所以字典序大的优先选,W->S->N->E,在dp值相同时按这个顺序取就行了。

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stdlib.h>
 4 //#include<math.h>
 5 #include<algorithm>
 6 //#include<iostream>
 7 using namespace std;
 8 
 9 int n,m,K;
10 #define maxn 1011
11 int f[66][66][66],pre[66][66][66],s[66][66];
12 struct Point{int x,y;}a[maxn];bool mp[maxn][maxn];
13 char c[66],ss[66];int ls,lss=0;
14 bool check()
15 {
16     if (!lss) return 1;
17     for (int i=ls-1;i>=0;i--)
18         if (c[i]!=ss[i]) return c[i]<ss[i];
19     return 0;
20 }
21 const int inf=0x3f3f3f3f;
22 int ans=0;
23 int main()
24 {
25     scanf("%d%d%d",&n,&m,&K);int x,y;
26     for (int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
27     for (int i=1;i<=m;i++) scanf("%d%d",&x,&y),mp[x][y]=1;
28     memset(s,0,sizeof(s));
29     for (int i=-30;i<=30;i++)
30         for (int j=-30;j<=30;j++)
31         {
32             for (int k=1;k<=n;k++)
33                 if (a[k].x+i>0 && a[k].x+i<1011 && a[k].y+j>0 && a[k].y+j<1011)
34                     if (mp[a[k].x+i][a[k].y+j]) s[i+30][j+30]++;
35         }
36     for (int i=0;i<=60;i++)
37         for (int j=0;j<=60;j++)
38             f[0][i][j]=-inf;
39     f[0][30][30]=0;
40     for (int k=1;k<=K;k++)
41         for (int i=-30;i<=30;i++)
42             for (int j=-30;j<=30;j++)
43             {
44                 int x=i+30,y=j+30;
45                 f[k][x][y]=-inf;
46                 if (x<60 && f[k-1][x+1][y]+s[x][y]>f[k][x][y])
47                 {
48                     f[k][x][y]=f[k-1][x+1][y]+s[x][y];
49                     pre[k][x][y]='W';
50                 }
51                 if (y<60 && f[k-1][x][y+1]+s[x][y]>f[k][x][y])
52                 {
53                     f[k][x][y]=f[k-1][x][y+1]+s[x][y];
54                     pre[k][x][y]='S';
55                 }
56                 if (y && f[k-1][x][y-1]+s[x][y]>f[k][x][y])
57                 {
58                     f[k][x][y]=f[k-1][x][y-1]+s[x][y];
59                     pre[k][x][y]='N';
60                 }
61                 if (x && f[k-1][x-1][y]+s[x][y]>f[k][x][y])
62                 {
63                     f[k][x][y]=f[k-1][x-1][y]+s[x][y];
64                     pre[k][x][y]='E';
65                 }
66                 if (k==K) ans=max(ans,f[k][x][y]);
67             }
68     printf("%d\n",ans);
69     for (int i=0;i<=60;i++)
70         for (int j=0;j<=60;j++)
71             if (f[K][i][j]==ans)
72             {
73                 int t=K,x=i,y=j;ls=0;
74                 while (t)
75                 {
76                     c[ls++]=pre[t][x][y];
77                     switch (pre[t][x][y])
78                     {
79                         case 'W':x++;break;
80                         case 'S':y++;break;
81                         case 'N':y--;break;
82                         case 'E':x--;break;
83                     }
84                     t--;
85                 }
86                 if (check()) memcpy(ss,c,sizeof(c)),lss=ls;
87             }
88     for (int i=ls-1;i>=0;i--) putchar(ss[i]);
89     return 0;
90 }
View Code

注意一下下标不要访问到-1或者61否则会出奇怪错误。

posted @ 2017-09-28 19:25  Blue233333  阅读(165)  评论(0编辑  收藏  举报