试题描述:
NOIP 王国有一个奇怪的监狱,这个监狱一共有 P 个牢房,这些牢房一字排开,第 i 个
紧挨着第 i+1 个(最后一个除外)。现在正好这个牢房是满的。 上级下发了一个释放名单,要求每天释放名单上的一个人。这可把看守WSM吓得不轻,
因为WSM知道,现在牢房中的 P 个人,可以相互之间传话。如果某个人离开了,那么原来 和这个人能说上话的人,都会非常气愤,导致他们那天会一直大吼大叫,搞得WSM很头疼。 如果给这些要发火的人吃上肉,他们就会安静点。 现在WSM想知道,如何安排释放顺序,才能使他们花费的肉钱最少。
输入格式:
第一行两个数 P 和Q,Q表示释放名单上的人数。 第二行Q个数,表示要释放哪些人。
输出格式:
仅一行,表示最少要给多少人次送肉吃。
数据规模:
1<=P<=1000;1<=Q<=100。
输入样例:
20 3 3 6 14
输出样例:
35
注意事项:
Q<=P;且 50%的数据 1<=P<=100;1<=Q<=5。
题解:
对于50%的数据,枚举释放顺序即可。
如果一个区间[x,y]中所有人都还在,现在区间中的z先被释放,代价就(y-x)(总数-1),且一旦z被释放,则区间变成了两个[x,z-1]和[z+1,y],此时释放其他的人,所支付的代价就之和这两个区间有关系了,且两个区间中的不同释放顺序是不会互相影响。
于是很容易想到基于这个区间的动态规划。
用f[i,j]表示在[i,j]区间所有该被释放的人都被释放的最小支付代价。很显然,需要枚举里面的哪个人最先被释放。假设释放了k,则代价就是f[i][k-1]+f[k+1][j]+(j-i)。动态规划转移就是f[i,j]=max{f[i][k-1]+f[k+1][j]+(j-i)}。
很显然动态规划的复杂度是O(P2Q)。这个复杂度就能通过本题的数据了。动态规划所涉及的值,它只和需要被释放人的序号ki以及ki±1有关。动态规划时只要考虑Q个状态就行了。这样复杂度就是:O(Q3)
上述算法的效率已经很高了,但是还可以优化。利用四边形不等式,可以把动态规划的复杂度优化到O(1),整体复杂度就是:O(Q2)。
时间复杂度:O(P^2Q) | O(Q^3) | O(Q^2)
空间复杂度:O(P^2) | O(Q^2)
代码:
#include<bits/stdc++.h>
#define F(i,a,b) for( int i=(a);i<=(b);i++ )
#define N 1001
#define M 10001
#define LL long long
#define oo 0x7fffffff
using namespace std;
int read()
{
int f=1,s=0;
char ch=getchar();
while( ch>'9' || ch<'0' ) { if( ch=='-' ) f=-1; ch=getchar(); }
while( ch<='9' && ch>='0' ) { s=( s<<1 )+( s<<3 )+ch-'0'; ch=getchar(); }
return f*s;
}
int m,n,k;
int tot,cnt,ans;
int p,q;
int a[N],low[N];
int d[N][N];
int dp( int l,int r )
{
if( d[l][r] ) return d[l][r];
d[l][r]=oo;
F( i,low[l],q )
{
k=a[i];
if( k>r )
break;
else d[l][r]=min( d[l][r],dp( l,k-1 )+dp( k+1,r )+r-l );
}
if( d[l][r]==oo ) d[l][r]=0;
return d[l][r];
}
int main()
{
//freopen( ".in","r",stdin );
//freopen( ".out","w",stdout );
p=read();
q=read();
F( i,1,q )
a[i]=read();
sort( a+1,a+n+1 );
int t=1;
F(i,1,p)
{
low[i]=t;
if( i==a[t] )
t++;
}
cout<<dp( 1,p )<<endl;
return 0;
}