【题解】「luoguP1514」引水入城
做题还得继续
下面讲一道复习型题目 先看题
说是要在湖边建水利设施 求能否到达最后一行并铺满 并求出设施数或无法到达区域数
说前三十分是无法铺满的
那么就输出样例可以使用floodfill 或者说用深广搜就可以拿到分数
比如这样

搜完之后扫一下最后一行看有没有干的
下面给出BFS代码
const int posx[] = { 1,0,-1,0 };
const int posy[] = { 0,1,0,-1 };
#define GOX b+posx[i]
#define GOY a+posy[i]
//g是记录高度的数组
inline void bfs(int start)
{
int a,b;
q.push(1);
q.push(start);
flag[1][start] = true;
if (n == 1)sit[start] = true;
for (; !q.empty();)
{
a = q.front();
q.pop();
b = q.front();
q.pop();
for (int i = 0; i < 4; i++)
{
if(GOX<=m&&GOX>0&&GOY<=n&&GOY>0)
if (g[GOY][GOX] < g[a][b])
{
if (flag[GOY][GOX] == false)
{
flag[GOY][GOX] = true;
q.push(GOY);
q.push(GOX);
if (GOY == n)
sit[GOX] = true;
}
}
}
}
}
如果铺的满呢
这里我们来思考一下
经过搜索后我们能知道什么
很明显我们知道了第一行每一个水利设施能覆盖的区域
如果只关注最后一行就是知道每个设施能覆盖的“段”
如果这个“段”是“线段”就好了
那么 这个“段”是否连续呢?
那么我们来一个简短不严谨的的证明
要想铺满的话“段"是否必须是连续的
首先我们制造一个不连续的“段” 像这样

或者这样

想一想会发现 这一个无法到达的区域会被水包围(这个我想了很久)
别的水利设施有水也会被这一个“圈”挡住

因此可知得知若能铺满,则设施所覆盖的区域必定为连续线段
因此便可以用贪心跑线段覆盖
解释一下
就是找能覆盖区间左端的线段中最长的 并将线段右端更新为区间左端 然后再去找
给出线段覆盖代码
for (; r > l;)
{
for (int i = 1; i <= m; i++)
{
if(l==1&&seg[i][0]==1)
temp = max(temp, seg[i][1]);
else if (seg[i][0] <= l+1&&l!=1)
temp = max(temp, seg[i][1]);
}
l = temp;
successcnt++;
temp = 0;
}
最后提醒一句 测试点#6 只有一行 很狗血 我的代码加了特判
下面给出完整代码
注意一下clear函数
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int posx[] = { 1,0,-1,0 };
const int posy[] = { 0,1,0,-1 };
#define GOX b+posx[i]
#define GOY a+posy[i]
queue<int>q;
int g[505][505];
int n, m;
int failcnt,successcnt;
bool flag[505][505];
int seg[505][2];
bool sit[505];
inline void clear()
{
for (; !q.empty();)
q.pop();
//这里没有清除第一行的flag 为什么?
//如果第一行的某个点被覆盖 则不需要用它起搜了 为什么?
for(int i=2;i<=n;i++)
memset(flag[i], false, sizeof(flag[i]));
if (n == 1)
memset(flag, false, sizeof(flag));
}
inline void bfs(int start)
{
int a,b;
q.push(1);
q.push(start);
flag[1][start] = true;
if (n == 1)sit[start] = true;
for (; !q.empty();)
{
a = q.front();
q.pop();
b = q.front();
q.pop();
for (int i = 0; i < 4; i++)
{
if(GOX<=m&&GOX>0&&GOY<=n&&GOY>0)
if (g[GOY][GOX] < g[a][b])
{
if (flag[GOY][GOX] == false)
{
flag[GOY][GOX] = true;
q.push(GOY);
q.push(GOX);
if (GOY == n)
sit[GOX] = true;
}
}
}
}
}
int main()
{
//freopen("in.txt", "r", stdin);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &g[i][j]);
for (int i = 1; i <= m; i++)
{
if (flag[1][i] == false)
{
clear();
bfs(i);
for (int j = 1; j <= m; j++)
if (flag[n][j] == true)
{
seg[i][0] = j;
break;
}
for (int j = m; j > 0; j--)
if (flag[n][j] == true)
{
seg[i][1] = j;
break;
}
}
}
for (int i = 1; i <= m; i++)
{
if (sit[i] == false)
failcnt++;
}
if (failcnt != 0)
{
printf("0\n%d", failcnt);
return 0;
}
int l, r;
l = 1;
r = m;
/*for (int i = 1; i <= m; i++)
{
if (seg[i][0] == 1)
{
l = max(l, seg[i][1]);
successcnt = 1;
}
}*/
int temp=0;
for (; r > l;)
{
for (int i = 1; i <= m; i++)
{
if(l==1&&seg[i][0]==1)
temp = max(temp, seg[i][1]);
else if (seg[i][0] <= l+1&&l!=1)
temp = max(temp, seg[i][1]);
}
l = temp;
successcnt++;
temp = 0;
}
printf("1\n%d", successcnt);
return 0;
}
事实上,如果不用上述clear函数
而是改为
memset(flag,false,sizeof(flag));
分析可知其最大时间复杂度为500x500x500=125000000=1.25x10^8
而NOIP数据这么水卡常也是可以过的

浙公网安备 33010602011771号