HDU-4412 Sky Soldiers(区间DP)

题意:有k个空兵跳伞,第i个空兵有Li个位置可能会降落,降落在第j个位置xi,j的概率为为Pi,j,(保证(Σ(j=1,Li)Pi,j)=1)现在需要在坐标轴放置m个任务点,每个空兵降落后会往离自己的任务点走去,求每个空兵行走距离总和的期望值最小是多少。

(1<=k<=1000,1<=50<=m,m<=∑(i=1,k)Li<=1000)

首先,如果有多个空兵可能落在同一个点上,他们落在这一点的概率可以累加,所以可以使用计数法,而坐标范围是整个int,故可以开一个int到double的映射,完成去重和累计两个操作。

如果不考虑点的权值(即概率和),那么在区间[i,j]中放置一个点使[i,j]中的元素到这个点距离和最小,这个点必然是i+j>>1。而有了权值后,这并不成立,但仍能保证这个点一定是[i,j]中的某个元素,而且如果j往右靠,这个点绝不会往左靠。换句话说,随着j的右移,这个点的坐标保持递增或保持不变。

那接下来就是简单的三层循环了,方程:dp[i][j]=min{dp[i-1][k]+dis[k+1][j]},dp[i][j]是[1,j]中放i个点的最短总行走距离,dis[i][j]是区间[i,j]中放一个点的总行走距离。

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<map>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define memclear(a) memset(a,0,sizeof(a))
#define N 1000
#define M 50
using namespace std;
map<int,double>f;
map<int,double>::iterator it;
double dis[N+3][N+3];
double dp[M+3][N+3];
double prb[N],sprb[N];
int coor[N];
int n,m;

int main()
{
    while(scanf("%d%d",&n,&m)&&(n||m))
    {
    	f.clear();
    	int s,x,cnt=0;double p;
    	FOR(i,1,n)
    	{
        	scanf("%d",&s);
    		FOR(i,1,s)
			{
		    	scanf("%d%lf",&x,&p);
		    	f[x]+=p;
			}
		}
		sprb[0]=0;
		for(it=f.begin();it!=f.end();it++)
		{
			cnt++;
			coor[cnt]=(*it).first;
			prb[cnt]=(*it).second;
			sprb[cnt]=sprb[cnt-1]+prb[cnt];
		}
		FOR(i,1,cnt)
		{
			int pos=i;
			FOR(j,i+1,cnt)
			{                      //pos为dis[i][j-1]取得最小值的任务点
				double sum=dis[i][j-1]+prb[j]*(coor[j]-coor[pos]);
				while(sprb[pos]-sprb[i-1]<sprb[j]-sprb[pos])
				{
					sum+=(coor[pos+1]-coor[pos])*((sprb[pos]-sprb[i-1])-(sprb[j]-sprb[pos]));
					pos++;
				}
				dis[i][j]=sum;
			}
		}
		FOR(i,1,cnt)dp[1][i]=dis[1][i];
		FOR(i,2,m)   //放i个任务点 
			FOR(j,i,cnt)  //前j个坐标
			{
				dp[i][j]=2e9;
				FOR(k,i-1,j-1)  //前k个坐标放了i-1个任务点
				    dp[i][j]=min(dp[i][j],dp[i-1][k]+dis[k+1][j]); 
			}
		printf("%.2lf\n",dp[m][cnt]); 
	}
    return 0;
}

猜你喜欢

转载自blog.csdn.net/paulliant/article/details/80216817
sky
今日推荐