您的位置:首页 > 其它

HDU4568题解

2018-04-07 15:23 190 查看

HDU4568题解

题意

给出一个矩阵,可以从任何一个边缘进入,从任何一个边缘离开,问拿走矩阵内所有财宝所需的最小路径,若不能拿走所有财宝,输出0。

笺释

这题本来应该是没什么难度的,要把所有藏宝点拿出来建图

for(int i=1;i<=k;i++)
{
bfs(x[i],y[i],i);
for(int j=1;j<=n;j++)
{
cost[i]=min(cost[i],dis[i][j][1]);
cost[i]=min(cost[i],dis[i][j][m]);
}
for(int j=1;j<=m;j++)
{
cost[i]=min(cost[i],dis[i][1][j]);
cost[i]=min(cost[i],dis[i]
[j]);
}
}


这里我用cost[i]记录下来i点到所有边界点的距离最小值,初始化的时候选择最小值进入,离开的时候选择最小值离开。

初始化

for(int i=1;i<=k;i++)
{
if(cost[i]!=INF)
{
dp[1<<(i-1)][i]=cost[i]+a[x[i]][y[i]];
}
}


离开

for(int i=1;i<=k;i++)
{
// printf("B%d %d\n",dp[(1<<k)-1][i],cost[i]);
if(dp[(1<<k)-1][i]!=INF)
{
ans=min(ans,dp[(1<<k)-1][i]+cost[i]);
}
}


但是这个题建图的时候要注意和HDU3481的区别,那道题中选择路径的时候最短路用bfs找就可以,因为每一次移动的花费都是相等的”1”,但是这道题必须用查找最短路的算法去做,我自己写了个类spfa算法,省去了建图的部分。

void bfs(int sx,int sy,int si)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
dis[si][i][j]=INF;
vis[i][j]=0;
}
}
if(a[sx][sy]==-1)
{
return;
}
dis[si][sx][sy]=0;
vis[sx][sy]=1;
queue<node>q;
q.push((node){sx,sy});
while(!q.empty())
{
node newnode=q.front();
q.pop();
int tx=newnode.x;
int ty=newnode.y;
vis[tx][ty]=0;
for(int i=1;i<=4;i++)
{
int nx=tx+mx[i];
int ny=ty+my[i];
if(legal(nx,ny))
{
if(dis[si][nx][ny]>(dis[si][tx][ty]+a[nx][ny]))
{
dis[si][nx][ny]=dis[si][tx][ty]+a[nx][ny];
//更新完之后如果点本身还在队列里 就不用重复进队列
//但是要注意到我们本身的nc是储存在队列里的 这个nc必须被更新 要不然就不用nc储存
if(!vis[nx][ny])
{
vis[nx][ny]=1;
q.push((node){nx,ny});
}
}
}
}
}
}


在写类spfa算法的时候有一个地方出错了很久,我本来是把到一个点处的花费c和点x,y作为一个结构体储存在bfs队列里的,这种写法是错的,因为起点s到队列里的一个点i的最短路径dis[s][i]有可能被队列里拿出的其他点更新,而我们队列里的c是没有更新的,所以用的时候只能用由dis[s][i]记录的那个数据,这样以来把c存在结构体里就没有任何意义了。

如果只是这样的话,本来应该能很快A掉,但是我位运算的括号又一次少加了。

if((i&(1<<(l-1)))!=0)
{
continue;
}


在主干dp里有这个条件判断要到达的点l是否已经在i里访问过,我漏掉了一个括号一直WA,自己当时却一直找不到问题所在,于是就乱想是不是因为对题意理解错了,碰巧这道题题意确实蛮模糊的。

Now give you a map of the area, you must calculate the least cost that James bring out all treasures he can take(one point up to only one treasure).Also, if nothing James can get, please output 0.


注意James bring out all treasures he can take,这句话,我理解成了“若他无法拿走所有财宝,他也要拿走他能拿走的最大财宝,也就是说,在dp的过程中,他要拿最大数目的财宝,这是第一要求,第二要求才是他在拿这个数目的财宝的过程中路径要最短。”

本着这样的理解,我增加了num数组用num[i][j]表示状态i起点j情况下所拿到的财宝的最大数目

if(num[i|(1<<(l-1))][l]<num[i][j]+1)
{
num[i|(1<<(l-1))][l]=num[i][j]+1;
dp[i|(1<<(l-1))][l]=dp[i][j]+dis[j][x[l]][y[l]];
}
if(num[i|(1<<(l-1))][l]==num[i][j]+1)
{
dp[i|(1<<(l-1))][l]=min(dp[i][j]+
4000
dis[j][x[l]][y[l]],dp[i|(1<<(l-1))][l]);
}


查找答案也就变成了这样

for(int i=0;i<=(1<<k)-1;i++)
{
for(int j=1;j<=k;j++)
{
if(ans1<num[i][j])
{
ans1=num[i][j];
ans2=dp[i][j]+cost[j];
}
if(ans1==num[i][j])
{
ans2=min(ans2,dp[i][j]+cost[j]);
}
}
}


和昨天的poj2288很像的思路。

然而是错的。

最后WA到无计可施了,才发现是自己少写了一个括号,然而几个小时已经过去了。



完整代码

#include<bits/stdc++.h>
#define MAXN 205
#define INF 0x3f3f3f3f
#define MAXK 15
using namespace std;
int t,n,m,a[MAXN][MAXN],dis[MAXK+1][MAXN][MAXN],cost[MAXK],k,dp[1<<MAXK][MAXK+1],x[MAXK+1],y[MAXK+1],mx[5]={0,1,-1,0,0},my[5]={0,0,0,1,-1},vis[MAXN][MAXN];
struct node
{
int x,y;
};
bool legal(int x,int y)
{
if(a[x][y]==-1)
{
return false;
}
if(!(x>=1&&x<=n))
{
return false;
}
if(!(y>=1&&y<=m))
{
return false;
}
return true;
}
void bfs(int sx,int sy,int si) { for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { dis[si][i][j]=INF; vis[i][j]=0; } } if(a[sx][sy]==-1) { return; } dis[si][sx][sy]=0; vis[sx][sy]=1; queue<node>q; q.push((node){sx,sy}); while(!q.empty()) { node newnode=q.front(); q.pop(); int tx=newnode.x; int ty=newnode.y; vis[tx][ty]=0; for(int i=1;i<=4;i++) { int nx=tx+mx[i]; int ny=ty+my[i]; if(legal(nx,ny)) { if(dis[si][nx][ny]>(dis[si][tx][ty]+a[nx][ny])) { dis[si][nx][ny]=dis[si][tx][ty]+a[nx][ny]; //更新完之后如果点本身还在队列里 就不用重复进队列 //但是要注意到我们本身的nc是储存在队列里的 这个nc必须被更新 要不然就不用nc储存 if(!vis[nx][ny]) { vis[nx][ny]=1; q.push((node){nx,ny}); } } } } } }
int main()
{
scanf("%d",&t);
while(t--)
{
memset(cost,0x3f,sizeof(cost));
memset(dp,0x3f,sizeof(dp));
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
}
}
scanf("%d",&k);
for(int i=1;i<=k;i++)
{
scanf("%d %d",&x[i],&y[i]);
x[i]++;y[i]++;
}
for(int i=1;i<=k;i++)
{
bfs(x[i],y[i],i);
for(int j=1;j<=n;j++)
{
cost[i]=min(cost[i],dis[i][j][1]);
cost[i]=min(cost[i],dis[i][j][m]);
}
for(int j=1;j<=m;j++)
{
cost[i]=min(cost[i],dis[i][1][j]);
cost[i]=min(cost[i],dis[i]
[j]);
}
}
for(int i=1;i<=k;i++)
{
if(cost[i]!=INF)
{
dp[1<<(i-1)][i]=cost[i]+a[x[i]][y[i]];
}
}
for(int i=0;i<=(1<<k)-1;i++)
{
//i=1 j=1
for(int j=1;j<=k;j++)
{
if((i&(1<<(j-1)))==0)
{
continue;
}
if(dp[i][j]==INF)
{
continue;
}
for(int l=1;l<=k;l++)
{
if((i&(1<<(l-1)))!=0)
{
continue;
}
if(j==l)
{
continue;
}
if(dis[j][x[l]][y[l]]==INF)
{
continue;
}
dp[i|(1<<(l-1))][l]=min(dp[i][j]+dis[j][x[l]][y[l]],dp[i|(1<<(l-1))][l]);
}
}
}
int ans=INF;
for(int i=1;i<=k;i++)
{
// printf("B%d %d\n",dp[(1<<k)-1][i],cost[i]);
if(dp[(1<<k)-1][i]!=INF)
{
ans=min(ans,dp[(1<<k)-1][i]+cost[i]);
}
}
printf("%d\n",ans==INF?0:ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  bfs