题意:有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; }