【NOIP2010提高组T4】引水入城-搜索+DP
2016-10-30 19:46
399 查看
测试地址:引水入城
做法:一开始可以把最上面一排都看成有水,用一次Floodfill判断是否有解,如果最下面一排没有被全部覆盖则无解。可以证明,如果有解,则从上面一个点流水到下面一定会覆盖下面的一个区间。用2次DFS找寻上面每个点的可覆盖区间的左右端点,在一次DFS中,走过的点不用再走,因为从这个点扩展到的点的端点肯定已经确定了。最后用一次DP找最小区间覆盖,设f[i]为要覆盖前i个点至少要建的蓄水厂数,则状态转移方程为:f[i]=f[left[v]-1]+1(left[v]<=i&&right[v]>=i),其中left[v]和right[v]表示上方第v个点所覆盖的区间的左右端点。
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
int n,m,h[510][510],l[510],r[510],ans,f[510];
bool vis[510][510];
typedef pair<int,int> p;
bool ava(int x,int y,int sx,int sy)
{
return x>=1&&x<=n&&y>=1&&y<=m&&!vis[x][y]&&h[x][y]<h[sx][sy];
}
bool ava2(int x,int y,int sx,int sy)
{
return x>=1&&x<=n&&y>=1&&y<=m&&!vis[x][y]&&h[x][y]>h[sx][sy];
}
void bfs()
{
queue<p> q;
memset(vis,0,sizeof(vis));
for(int i=1;i<=m;i++)
vis[1][i]=1,q.push(make_pair(1,i));
while(!q.empty())
{
int x=q.front().first,y=q.front().second;
q.pop();
if (ava(x-1,y,x,y)) q.push(make_pair(x-1,y)),vis[x-1][y]=1;
if (ava(x,y-1,x,y)) q.push(make_pair(x,y-1)),vis[x][y-1]=1;
if (ava(x+1,y,x,y)) q.push(make_pair(x+1,y)),vis[x+1][y]=1;
if (ava(x,y+1,x,y)) q.push(make_pair(x,y+1)),vis[x][y+1]=1;
}
}
void dfsl(int x,int y,int i)
{
vis[x][y]=1;
if (x==1) l[y]=i;
if (ava2(x-1,y,x,y)) dfsl(x-1,y,i);
if (ava2(x,y-1,x,y)) dfsl(x,y-1,i);
if (ava2(x+1,y,x,y)) dfsl(x+1,y,i);
if (ava2(x,y+1,x,y)) dfsl(x,y+1,i);
}
void dfsr(int x,int y,int i)
{
vis[x][y]=1;
if (x==1) r[y]=i;
if (ava2(x-1,y,x,y)) dfsr(x-1,y,i);
if (ava2(x,y-1,x,y)) dfsr(x,y-1,i);
if (ava2(x+1,y,x,y)) dfsr(x+1,y,i);
if (ava2(x,y+1,x,y)) dfsr(x,y+1,i);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&h[i][j]);
bfs();
ans=0;
for(int i=1;i<=m;i++)
if (!vis
[i]) ans++;
if (ans>0) {printf("0\n%d",ans);return 0;}
memset(vis,0,sizeof(vis));
for(int i=1;i<=m;i++)
if (!vis
[i]) dfsl(n,i,i);
memset(vis,0,sizeof(vis));
for(int i=m;i>=1;i--)
if (!vis
[i]) dfsr(n,i,i);
f[0]=0,f[1]=1;
for(int i=2;i<=m;i++)
{
f[i]=999999999;
for(int j=1;j<=m;j++)
{
if (l[j]<=i&&r[j]>=i) f[i]=min(f[i],f[l[j]-1]+1);
else if (l[j]>i) break;
}
}
printf("1\n%d",f[m]);
return 0;
}
做法:一开始可以把最上面一排都看成有水,用一次Floodfill判断是否有解,如果最下面一排没有被全部覆盖则无解。可以证明,如果有解,则从上面一个点流水到下面一定会覆盖下面的一个区间。用2次DFS找寻上面每个点的可覆盖区间的左右端点,在一次DFS中,走过的点不用再走,因为从这个点扩展到的点的端点肯定已经确定了。最后用一次DP找最小区间覆盖,设f[i]为要覆盖前i个点至少要建的蓄水厂数,则状态转移方程为:f[i]=f[left[v]-1]+1(left[v]<=i&&right[v]>=i),其中left[v]和right[v]表示上方第v个点所覆盖的区间的左右端点。
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
int n,m,h[510][510],l[510],r[510],ans,f[510];
bool vis[510][510];
typedef pair<int,int> p;
bool ava(int x,int y,int sx,int sy)
{
return x>=1&&x<=n&&y>=1&&y<=m&&!vis[x][y]&&h[x][y]<h[sx][sy];
}
bool ava2(int x,int y,int sx,int sy)
{
return x>=1&&x<=n&&y>=1&&y<=m&&!vis[x][y]&&h[x][y]>h[sx][sy];
}
void bfs()
{
queue<p> q;
memset(vis,0,sizeof(vis));
for(int i=1;i<=m;i++)
vis[1][i]=1,q.push(make_pair(1,i));
while(!q.empty())
{
int x=q.front().first,y=q.front().second;
q.pop();
if (ava(x-1,y,x,y)) q.push(make_pair(x-1,y)),vis[x-1][y]=1;
if (ava(x,y-1,x,y)) q.push(make_pair(x,y-1)),vis[x][y-1]=1;
if (ava(x+1,y,x,y)) q.push(make_pair(x+1,y)),vis[x+1][y]=1;
if (ava(x,y+1,x,y)) q.push(make_pair(x,y+1)),vis[x][y+1]=1;
}
}
void dfsl(int x,int y,int i)
{
vis[x][y]=1;
if (x==1) l[y]=i;
if (ava2(x-1,y,x,y)) dfsl(x-1,y,i);
if (ava2(x,y-1,x,y)) dfsl(x,y-1,i);
if (ava2(x+1,y,x,y)) dfsl(x+1,y,i);
if (ava2(x,y+1,x,y)) dfsl(x,y+1,i);
}
void dfsr(int x,int y,int i)
{
vis[x][y]=1;
if (x==1) r[y]=i;
if (ava2(x-1,y,x,y)) dfsr(x-1,y,i);
if (ava2(x,y-1,x,y)) dfsr(x,y-1,i);
if (ava2(x+1,y,x,y)) dfsr(x+1,y,i);
if (ava2(x,y+1,x,y)) dfsr(x,y+1,i);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&h[i][j]);
bfs();
ans=0;
for(int i=1;i<=m;i++)
if (!vis
[i]) ans++;
if (ans>0) {printf("0\n%d",ans);return 0;}
memset(vis,0,sizeof(vis));
for(int i=1;i<=m;i++)
if (!vis
[i]) dfsl(n,i,i);
memset(vis,0,sizeof(vis));
for(int i=m;i>=1;i--)
if (!vis
[i]) dfsr(n,i,i);
f[0]=0,f[1]=1;
for(int i=2;i<=m;i++)
{
f[i]=999999999;
for(int j=1;j<=m;j++)
{
if (l[j]<=i&&r[j]>=i) f[i]=min(f[i],f[l[j]-1]+1);
else if (l[j]>i) break;
}
}
printf("1\n%d",f[m]);
return 0;
}
相关文章推荐
- 洛谷P1514 [NOIP2010提高组T4]引水入城
- 【dp】NOIP2010提高组引水入城
- NOIP 2010 引水入城 (BFS,DP)
- 【dp】NOIP2010提高组乌龟棋
- NOIP2010提高组复赛 解题报告(C/C++)(机械翻译)(乌龟棋)(关押罪犯)(引水入城)
- [Wikioi 1066][NOIP 2010提高组]引水入城
- NOIP2010 引水入城 解题报告(bfs+dp)
- [NOIP2010] 提高组 洛谷P1514 引水入城
- NOIP2010 提高组 复赛 flow 引水入城
- 【NOIP 2010 提高组 T2】乌龟棋(DP)
- 【NOIP2010】【贪心】【覆盖问题】T4 引水入城 题解
- 记忆化搜索+dp(洛谷1514 引水入城2010noip提高组)
- NOIP 提高组 2010 引水入城
- 2010年NOIP全国联赛提高组 T4 引水入城
- NOIP 2010 T4 浅谈如何“引水入城”,泛洪填充法BFS架构FLOODFILL及一类区间覆盖问题贪心求法
- noip2010引水入城-搜索+贪心
- 【NOIP2004提高组T4】虫食算-搜索剪枝
- |洛谷|NOIP2010|搜索|贪心|P1514 引水入城
- NOIP 2010 - 提高组 引水入城 棋盘上的BFS+区间覆盖(贪心) 重庆一中高2018级竞赛班第十次测试 2016.9.16 Problem 4
- NOIP2010提高组-引水入城