您的位置:首页 > 其它

Codeforces 677D Vanya and Treasure【dp+极限思维剪枝】

2017-01-29 18:59 281 查看
D. Vanya and Treasure

time limit per test
1.5 seconds

memory limit per test
256 megabytes

input
standard input

output
standard output

Vanya is in the palace that can be represented as a grid
n × m. Each room contains a single chest, an the room located in the
i-th row and j-th columns contains the chest of type
aij. Each chest of type
x ≤ p - 1 contains a key that can open any chest of type
x + 1, and all chests of type
1 are not locked. There is exactly one chest of type
p and it contains a treasure.

Vanya starts in cell (1, 1) (top left corner). What is the minimum total distance Vanya has to walk in order to get the treasure? Consider the distance between cell
(r1, c1) (the cell in the row
r1 and column
c1) and
(r2, c2) is equal to
|r1 - r2| + |c1 - c2|.

Input
The first line of the input contains three integers n,
m and p (1 ≤ n, m ≤ 300, 1 ≤ p ≤ n·m) — the number of rows and columns in the table representing the palace
and the number of different types of the chests, respectively.

Each of the following n lines contains
m integers aij (1 ≤ aij ≤ p) — the types of the chests in corresponding rooms.
It's guaranteed that for each x from
1 to p there is at least one chest of this type (that is, there exists a pair of
r and c, such that
arc = x). Also, it's guaranteed that there is exactly one chest of type
p.

Output
Print one integer — the minimum possible total distance Vanya has to walk in order to get the treasure from the chest of type
p.

Examples

Input
3 4 3
2 1 1 1
1 1 1 1
2 1 1 3


Output
5


Input
3 3 9
1 3 58 9 7
4 6 2


Output
22


Input
3 4 12
1 2 3 4
8 7 6 59 10 11 12


Output
11


题目大意:

一个N*M的地图上,布满了宝箱,任务就是得到编号为P的宝箱中的物品,对应我们打开了aij号宝箱,那么我们接下来就可以开aij+1号宝箱了。

所有编号为1的宝箱都是开着的,不需要钥匙。

我们最开始在左上角,问最少需要走多长的路径,就能完成任务。

思路:

1、考虑dp,设定dp【i】【j】表示开了(i,j)位子上的宝箱最少需要的路径长度。

2、那么不难推出其状态转移方程:

dp【i】【j】=min(dp【i】【j】,dp【ii】【jj】+abs(i-ii)+abs(j-jj));【此时保证a(ii,jj)+1==a(i,j)】;

但是直接暴力肯定是不行的,那么我们需要一些优化和极思维:

①我们用vector来存每个aij值都落在哪些位子上。

②考虑到P越大,对于每两个相邻编号的宝箱量,其乘积越大,需要枚举量就越大。那么当P越小的时候,假设N==300,M==300,P==3,分布1的个数为30000.分布2的个数为30000.分布3的个数也是30000的情况下,需要枚举的量是很大的。

③那么我们考虑极限思想,对于一条路径来说,肯定对于一个点(i,j)来讲,我们是很希望由dp【ii】【jj】小的来转移,那么我们不妨大胆设想,在枚举点(ii,jj)之前,将vector按照dp值从小到大排序一下,然后枚举一个量,作为最大枚举量。我一开始设定的5000.慢慢减小,到了3000就Ac了.接下来继续枚举,直到1000还是Ac的.

那么我们既然能够通过这种极限思想来剪枝Ac掉,也是很不错的做法。

Ac代码:

#include<stdio.h>
#include<string.h>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
int dis,pos;
}b[3050000],now;
int a[305][305];
int dp[305][305];
int cmp(node a,node b)
{
return a.dis<b.dis;
}
vector<node >mp[308*308];
int main()
{
int n,m,p;
while(~scanf("%d%d%d",&n,&m,&p))
{
for(int i=1;i<=p;i++)mp[i].clear();
for(int i=0;i<n;i++)for(int j=0;j<m;j++)dp[i][j]=0x3f3f3f3f;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
scanf("%d",&a[i][j]);
if(a[i][j]==1)
{
dp[i][j]=i+j;
now.dis=dp[i][j];
}
now.pos=i*m+j+1;
mp[a[i][j]].push_back(now);
}
}
sort(mp[1].begin(),mp[1].end(),cmp);
for(int i=2;i<=p;i++)
{
int size1=mp[i].size();
int size2=mp[i-1].size();
for(int j=0;j<size1;j++)
{
int x=mp[i][j].pos/m,y=mp[i][j].pos%m-1;
if(y<0)y=m-1,x--;
for(int k=0;k<size2&&k<1000;k++)
{
int xx=mp[i-1][k].pos/m,yy=mp[i-1][k].pos%m-1;
if(yy<0)yy=m-1,xx--;
dp[x][y]=min(dp[x][y],dp[xx][yy]+abs(x-xx)+abs(y-yy));
}
mp[i][j].dis=dp[x][y];
}
sort(mp[i].begin(),mp[i].end(),cmp);
}
int output=0x3f3f3f3f;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(a[i][j]==p)
output=min(dp[i][j],output);
}
}
printf("%d\n",output);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Codeforces 677D