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

来源:互联网 发布:淘宝男款外套 编辑:程序博客网 时间:2024/05/02 16:28

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 thei-th row and j-th columns contains the chest of typeaij. Each chest of typex ≤ p - 1 contains a key that can open any chest of typex + 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 rowr1 and columnc1) 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 ofr and c, such thatarc = x). Also, it's guaranteed that there is exactly one chest of typep.

Output

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

Examples
Input
3 4 32 1 1 11 1 1 12 1 1 3
Output
5
Input
3 3 91 3 58 9 74 6 2
Output
22
Input
3 4 121 2 3 48 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);    }}








0 0