P1514引水入城解题报告
无秒
2020-04-28 20:06:38
看到这题我就想着最基础的是搜索。但是搜索了之后只知道从一个临近湖边点能够到达干旱地区的点,就算储存了以后还要一个个搜索来求出最小值,太麻烦了。因为要用到数组,自然可以加上dp。
首先这很明显是bfs,搜的对象就是从一个最高点开始(也就是只可以出去的,引水引不进的).按照前面的思路,我需要一个v[i]表示蓄水厂可以到这个点吗(0个的话就直接++),tu[i]存图,bfs时用上一个结构体存点,当做队列。以及一个结构体,包含一排中的左端点(能到的最左的点)以及右端点,还有值v(也是判断这个临近湖的点能否被到达,如果能自然就不用建蓄水厂了),和判断是否经过的数组f[i]。dp时用d[i]表示1—n最多要多少蓄水厂,g[i][j]表示从i到j的需要蓄水厂数。所以dp方程就是d[i]=min(d[i],d[j]+g[j+1][i])。
那些啥的我都打到代码里了,因为我发现这样直接对每条语句解释更加简洁明了。
```cpp
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
int n,m,sum,dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int tu[501][501],d[501],f[501][501],v[1000001],g[501][501];
struct nodea{
int r,l,v;
}a[1000001];
struct nodeb{
int x,y;
}b[1000001];
int bfs(int ax,int by)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
f[i][j]=0;
if(n==1){
v[by]=1;
a[by].l=a[by].r=by;
}//当它只有一行时,就可以直接赋值了
int h=1,r=2,X,Y;
b[1].x=ax;b[1].y=by;f[ax][by]=1;
while(h<r){
for(int i=0;i<=3;i++){
X=b[h].x+dx[i];Y=b[h].y+dy[i];
if(tu[X][Y]<tu[b[h].x][b[h].y]&&f[X][Y]==0){
if(X>=1&&X<=n&&Y>=1&&Y<=m)//判断条件就不用我说了吧
{
if(X==1) a[Y].v=0;
//当它可以到达新的临近湖的点,那么这个点肯定不用建厂,因为这个点可以被到达
if(X==n){
v[Y]=1;
a[by].l=min(a[by].l,Y);
a[by].r=max(a[by].r,Y);
}//更新
b[r].x=X;
b[r].y=Y;
f[X][Y]=0;
r++;
}
}
}
h++;
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&tu[i][j]);
for(int i=1;i<=m;i++){
a[i].l=99999999;
a[i].r=0;
a[i].v=1;
v[i]=0;
}
for(int i=1;i<=m;i++)
if(a[i].v==1) bfs(1,i);
//对于每个可以用的点进行宽搜
for(int i=1;i<=m;i++)
if(v[i]==0) sum++;
if(sum!=0){
printf("0\n%d",sum);
return 0;
}//假如有不可以到的点就直接++输出就行
for(int i=1;i<=m;i++)
{
d[i]=99999999;
for(int j=1;j<=m;j++)
g[i][j]=99999999;
}
for(int t=1;t<=m;t++)
if(a[t].r!=0)
for(int i=a[t].l;i<=a[t].r;i++)
for(int j=i;j<=a[t].r;j++)
{
g[i][j]=1;
if(i==1) d[j]=1;
}//把左端点到右端点赋值为0
for(int i=1;i<=m;i++)
for(int j=1;j<=i-1;j++)
d[i]=min(d[i],d[j]+g[j+1][i]);//转移方程
printf("1\n%d",d[m]);
return 0;
}
```
PS:这题贼坑的就是4,5的点,感觉莫名其妙,数据是一堆递减数字再加上一堆1……(大佬可以帮我看一下……)