您的位置:首页 > 其它

codeforces 677D

2016-07-18 20:50 232 查看
强行暴力水过……二维线段树真的长,我实在是不想写了.暴力1k左右,而二维线段树5k……

题目大意

一个N*M的网格图,每个格子有一把钥匙,上面一个权值

要求从 1到 P按顺序收集钥匙,保证 P只有一把

问收集全部钥匙的最短距离是多少

做法1:大概就是维护用纵列维护一个dis,用横列维护一个dp,用vis维护他得到的钥匙的序号,有才更新,感觉很巧妙.

状态转移方程

位置(r,c),更新dis[r][k]=min(dis[r][k],ans[r][c]+abs(c-k)),1<=k<=m。

ans[r][c]=min(ans[r][c],dis[k][c]+abs(c-r)),1<=k<=n

代码

#include <cstdio>
#include <cmath>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair <int,int> mk;
int n,m,k,dp[305][305],vis[305][305],dis[305][305];
int t,len;
vector <mk>b[300*300+10];
void readdata()
{
scanf("%d%d%d",&n,&m,&k);
memset(dp,63,sizeof(dp));
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
scanf("%d",&t);
b[t].push_back(mk(i,j));
}
b[0].push_back(mk(1,1));
for (int i=0;i<=k;i++)
{
len=b[i].size();
for (int j=0;j<len;j++)
{
int r=b[i][j].first; int c=b[i][j].second;
if (r==1&&c==1&&dp[r][c]==0)
{
dp[r][c]=1000000000;
}

for (t=1;t<=n;t++)
{
if (vis[t][c]==i)
dp[r][c]=min(dp[r][c],dis[t][c]+abs(r-t));
}
}
for (int j=0;j<len;j++)
{
int r=b[i][j].first; int c=b[i][j].second;
for (int t=1;t<=m;t++)
{
if (vis[r][t]!=i+1)
{
vis[r][t]=i+1;
dis[r][t]=dp[r][c]+abs(c-t);
}
else
{
dis[r][t]=min(dis[r][t],dp[r][c]+abs(c-t));
}
}

}
}
cout<<dp[b[k][0].first][b[k][0].second];
}
int main()
{
readdata();
}


做法2

首先大家都应该能想的o(n^4)的暴力吧,我们只不过是用二维线段树或二维树状数组优化成o(n^2*logn^2)而已,但代码量大了不少.

状态转移方程

if(pi>=i&&pj>=j)dp[i][j]=min(dp[i][j],dp[pi][pj]+pi+pj-(i+j));

if(pi>=i&&pj<=j)dp[i][j]=min(dp[i][j],dp[pi][pj]+pi-pj-(i-j));

if(pi<=i&&pj>=j)dp[i][j]=min(dp[i][j],dp[pi][pj]+pj-pi-(j-i));

if(pi<=i&&pj<=j)dp[i][j]=min(dp[i][j],dp[pi][pj]-(pi+pj)+(i+j));

代码就不贴了(我才不会告诉你是因为我没调出来)

这道题方法很多,如暴力+bfs等,在此不再赘述(毕竟蒟蒻搞不懂)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: