办证(坐标型动规)

Description

     某人要办个签证,办证处是一座 M 层的大楼,每层楼都有 N 个办公室,编号为1..N,每个办公室有一个签证员,签证需要让第 M 层的某个签证员盖章才有效。每个签证员都要满足下面三个条件之一才会给他盖章:

  1. 这个签证员在1楼。
  2. 这人的签证已经给这个签证员的正楼下(房间号相同)的签证员盖过章了。
  3. 这人的签证已经给这个签证员的相邻房间(房间号相差1,楼层相同)的签证员盖过章了。 

  每个签证员盖章都要收取一定费用,这个费用不超过1000000000。找出费用最小的盖章路线,使签证生效。

Input

  第1行两个整数 M 和 N 。  接下来M行每行 N 个整数,第i行第j个数表示第i层的第j个签证员收取的费用。

Output

  输出最小的费用。

设f(i,j)表示从一楼到(i,j)的最小费用。则有一下几种:

一,直接从楼下上到楼上:

           t1=f(i-1,j)+a[i][j]

二,从左边过来:设前缀和sum[i][j]表示第i层楼1~j号房间收费总额。

           t2=max(f(i-1,k)+sum_{j}-sum_{k-1}|1<=k<j)

三,从右过来:

           t3=max(f(i-1,k)+sum_{k}-sum_{j-1}|j<k<=n)

最后作出决策:

           f(i,j)=max(t1,t2,t3)

另:据说此题还可以通过刷两次来使时间复杂度降为O(n*m)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int MAXN=505;
const int MAXM=105;
const LL INF=2000000000000;
int N,M;
LL a[MAXM][MAXN],sum[MAXM][MAXN],f[MAXM][MAXN];
int main()
{
	scanf("%d%d",&M,&N);
	for(int i=1;i<=M;i++)
	for(int j=1;j<=N;j++)
	{
		scanf("%lld",&a[i][j]);
		sum[i][j]=sum[i][j-1]+a[i][j];
		f[i][j]=INF;
	}
		
	for(int i=1;i<=N;i++)
		f[1][i]=a[1][i];
	
	for(int i=2;i<=M;i++)
	for(int j=1;j<=N;j++)
	{
		LL t=f[i-1][j]+a[i][j];
		for(int k=1;k<j;k++)
			t=min(t,f[i-1][k]+sum[i][j]-sum[i][k-1]);
		for(int k=j+1;k<=N;k++)
			t=min(t,f[i-1][k]+sum[i][k]-sum[i][j-1]);
		f[i][j]=t;
	}
	
	LL ans=INF;
	for(int i=1;i<=N;i++)
		ans=min(ans,f[M][i]);
	printf("%lld",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/WWWengine/article/details/81159844