题解 P2960 【[USACO09OCT]Milkweed的入侵Invasion of the Milkweed】

· · 题解

这是一道对懒得看题意的我 没错就是我人非常不友好的一道题

令人发指的横坐标与纵坐标 与 起点坐标 坑了我很久

看了讨论区的帖子才知道自己的问题

话不多说了 先看第一种解法

递推

#include<bits/stdc++.h>
using namespace std;
int f[105][105],tot=1,n,m,sx,sy,Time=1,sum;
char Map[105][105];
int l[8][2]={{1,0},{0,1},{-1,0},{0,-1},{-1,-1},{1,-1},{-1,1},{1,1}};
int main()
{
    memset(f,-1,sizeof(f));
    cin>>m>>n>>sy>>sx;
    for(int i=n;i>0;i--)
     for(int j=1;j<=m;j++)
       {
          cin>>Map[i][j];
          if(Map[i][j]=='.') sum++;
       }
    f[sx][sy]=0; 
    while(tot!=sum)
    {
        for(int i=1;i<=n;i++)
           for(int j=1;j<=m;j++)
              {
                 bool flag=0;
                     if(f[i][j]>0) continue;
                 if(Map[i][j]=='*') continue;
                 for(int q=0;q<8;q++)
                 {
                 int tx=i+l[q][0];
                 int ty=j+l[q][1];
                 if(tx<1||tx>n||ty<1||ty>m) continue;
                 if(Map[tx][ty]=='*') continue;
                 if(f[tx][ty]!=Time-1) continue;
                 f[i][j]=Time; flag=1;
                 }
                 if(flag) tot++;
           }
        Time++;
    }
    cout<<Time<<endl;
    return 0;
}

sum表示草的数量 tot表示已经占领的草的数量

Time 表示已经到第几天了

f 数组存储着每个草点第一次被占领的时间

第一层循环表示 : 如果还有草地没被占领 (tot!=sum)

就继续扩展

第二三层循环枚举点

显然点本身不能是石头且没被占领过

第四层循环枚举邻近的点

如果旁边有被占领的点并且它是被上一次扩展占领的

那么就标记这个点 被占领的点(tot)++

结果

似乎令人悲愤

开了o2也只有18分

不过细想实际上是情理之中

分析一下算法 :

后三重循环都是确定的(1001008)

只有第一重循环不确定(时间)

当时间多起来的时候 程序就跑的很慢很慢

但是反而在第十个点 n m 大 但是时间小的时候

递推还是跑的可以的

深搜

有宽搜AC的地方就会有深搜的骗分记录

本蒟蒻用深搜尝试了一下

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
int n,m,sx,sy,ans[105][105];
char Map[105][105];
int l[8][2]={{1,0},{0,1},{-1,0},{0,-1},{1,1},{-1,1},{1,-1},{-1,-1}};
inline void dfs(int x,int y,int step)
{
    ans[x][y]=step;
    int tx,ty;
    for(register int i=0;i<8;i++)
    {
        tx=x+l[i][0]; ty=y+l[i][1];
        if(tx<1||tx>n||ty<1||ty>m) continue;
        if(Map[tx][ty]=='*') continue;
        if(step+1>=ans[tx][ty]) continue;
        dfs(tx,ty,step+1);
    }
}
int main()
{
    memset(ans,0x3f,sizeof(ans));
    cin>>m>>n>>sy>>sx;
    for(register int i=n;i>0;i--)
      for(register int j=1;j<=m;j++)
        cin>>Map[i][j];
    dfs(sx,sy,0); 
    int cnt=0;
    for(register int i=1;i<=n;i++)
      for(register int j=1;j<=m;j++)
        if(Map[i][j]!='*') cnt=max(cnt,ans[i][j]);
    cout<<cnt<<endl;
    return 0;
}

基本上用深搜写迷宫是要用记忆化的

用一个数组来记录最小到达的时间

假设 : 你走到这用了x步 但是有人用更少的步数走到了这里过

你还要继续走吗 ? 你继续走会使答案变小吗?

显然不会的 那么遇到这种情况就别搜了

这就叫最优解剪枝

最后一遍循环寻找最大值

结果

似乎令人忧伤

不开o2 63分 吸氧后 90

即使我再加了一些register inline 但是也没有用

望哪位大佬来帮我修到AC

正解 ———— 宽搜

实际上蒟蒻一开始就想到宽搜

但是为了尝试提供新的思路 连续失败多次

发现这题还是得用宽搜

#include<bits/stdc++.h>
using namespace std;
char Map[105][105];
int n,m,l[8][2]={{1,0},{0,1},{-1,0},{0,-1},{1,1},{-1,-1},{1,-1},{-1,1}};
bool vis[105][105];
struct mmp
{
    int x,y,step;
}f,v;
queue <mmp> q; 
int bfs()
{
    int tot=0;
    f.step=0; q.push(f);
    while(!q.empty())
    {
        f=q.front();
        q.pop(); tot=max(tot,f.step);
        for(int i=0;i<8;i++)
        {
            v.x=f.x+l[i][0];
            v.y=f.y+l[i][1];
            v.step=f.step+1;
            if(v.x<1||v.x>n||v.y<1||v.y>m) continue;
            if(vis[v.x][v.y]) continue;
            if(Map[v.x][v.y]=='*') continue;
            q.push(v); vis[v.x][v.y]=1; 
        }
    }
    return tot;
}
int main()
{
    cin>>m>>n>>f.y>>f.x;
    vis[f.x][f.y]=1;
    for(int i=n;i>0;i--)
     for(int j=1;j<=m;j++)
      cin>>Map[i][j];
    cout<<bfs()<<endl;
    return 0;
}

洛谷缩进有小小的毛病但是别怪我哦QAQ

利用宽搜 我们只用输出最后出队列的点的步数就够了

主要是细节上的问题 不细心是要排错一会儿的

小小的总结

实际上只打算发宽搜的程序

但是洛谷多篇重复

蒟蒻就另辟蹊径 用其他稍劣的方法拿部分分

实际上就是想告诉看到这篇题解的大佬

一道题不要局限自己的一种思维

有想法就要大胆的去实现

这样做一题比做十题相同解法的题目的效果要好得多

最后感谢耐心看完蒟蒻题解的大佬 !!!

谢谢您尊重我的劳动成果