2019内蒙古大学生程序设计竞赛G
链接:https://www.bttcacm.cn/problem.php?id=1785
思路:是一道不错的dp题,很显然概率大的应该放前面扫描,dp(i,j)表示前i个区域分成j组的期望,显然状态转移方程就是dp[i][j]=min(dp[i][j],dp[k][j-1]+i*(sum[i]-sum[k]));此处的k是第k个之后到第i个分为一组。用到了前缀和求区间值的思想,总体来说比赛时还是有点难想的。
AC代码:
#include<bits/stdc++.h>
#define INF 0x3F3F3F3F
#define endl '\n'
#define css(n) cout<<setiosflags(ios::fixed)<<setprecision(n);
using namespace std;
typedef long long ll;
const int maxn=105;
int n,m,z;
int t;
double a,b;
double p[maxn];
double sum[maxn];
double dp[maxn][maxn];
bool comp(double a,double b)
{
return a>b;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&z);
for(int i=0;i<=n;i++)
for(int j=0;j<=z;j++)
dp[i][j]=INF;
memset(p,0,sizeof(p));
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++)
{
cin>>p[i];
}
sort(p+1,p+1+n,comp);
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+p[i]*1.0/100.0;
}
dp[0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i&&j<=z;j++)
{
dp[i][j]=INF;
for(int k=0;k<=i;k++)
{
dp[i][j]=min(dp[i][j],dp[k][j-1]+i*(sum[i]-sum[k]));
}
}
}
printf("%.3lf\n",dp[n][z]);
}
return 0;
}