P1514 [NOIP2010 提高组] 引水入城
考察:记忆化搜索
思路:
注意这道题只需要覆盖最下面一行的点即可....对于每个第一行的点求出它能覆盖的最后一行的最左边和最右边.然后做区间覆盖即可.如果一个个点求会TLE.我们可以发现如果确定了一个点为起点,它能覆盖的左右端点都是确定的.所以可以用记忆化搜索.
但是这道题如何标记搜过的点对程序的优化不同.如果以左右端点是否被更新来标记,在某个数据点会dfs109次....因为存在不能去最后一行的点.此时会不断搜索一遍.所以需要用bool数组标记.
1 #include <iostream> 2 #include <cstring> 3 #include <queue> 4 #include <algorithm> 5 using namespace std; 6 typedef pair<int,int> PII; 7 const int N = 510; 8 int n,m,mp[N][N],L[N][N],R[N][N]; 9 int xx[4] = {-1,1,0,0},yy[4] = {0,0,-1,1}; 10 bool vis[N][N]; 11 PII p[N]; 12 void dfs(int x,int y) 13 { 14 if(vis[x][y]) return; 15 vis[x][y] = 1; 16 for(int i=0;i<4;i++) 17 { 18 int dx = x+xx[i],dy = y+yy[i]; 19 if(dx>=1&&dx<=n&&dy>=1&&dy<=m&&mp[dx][dy]<mp[x][y]) 20 { 21 if(!vis[dx][dy]) dfs(dx,dy); 22 L[x][y] = min(L[dx][dy],L[x][y]); 23 R[x][y] = max(R[dx][dy],R[x][y]); 24 } 25 } 26 } 27 void solve() 28 { 29 for(int i=1;i<=m;i++) 30 { 31 p[i].first = L[1][i]; 32 p[i].second = R[1][i]; 33 } 34 int ed = m,ans = 0,st = 1; 35 sort(p+1,p+m+1); 36 bool ok =1; 37 for(int i=1,j=1;i<=m;i++) 38 { 39 int r = -2e9; 40 while(j<=m&&p[j].first<=st) r = max(p[j].second,r),j++; 41 if(r>=ed) {ans++;ok = 0;break;} 42 if(r<st) {ans =-1;break;} 43 st = r+1; 44 ans++; 45 i = j-1; 46 } 47 if(!ok) printf("1\n%d\n",ans); 48 else 49 { 50 int cnt = m; 51 for(int i=1;i<=m;i++) 52 cnt-=vis[n][i]; 53 printf("0\n%d\n",cnt); 54 } 55 } 56 int main() 57 { 58 scanf("%d%d",&n,&m); 59 for(int i=1;i<=n;i++) 60 for(int j=1;j<=m;j++) scanf("%d",&mp[i][j]); 61 memset(L,0x3f,sizeof L); memset(R,-1,sizeof R); 62 for(int i=1;i<=m;i++) L[n][i] = i,R[n][i] = i; 63 for(int i=1;i<=m;i++) 64 if(!vis[1][i]) dfs(1,i); 65 solve(); 66 return 0; 67 }