链接
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4202
题解
这题条件有点多,但是我们给它一层层剥开就发现这个题其实没那么复杂
集合划分显然是不一定的,但是
n=100
n
=
100
似乎不是状压
DP
D
P
,所以说我们不可能去枚举集合
但是不难发现,任何一种方案,都可以理解成把原序列进行一定的排序后,划分成恰好
m
m
个区间,最小代价是多少
那么立刻就能想到一种做法:随机化顺序若干次,每次做这样的DP:f[i][j]表示前个位置恰好划分成了
j
j
个区间的最小代价
但是每次是
O(n3)
O
(
n
3
)
的,不能进行太多次数的随机化,况且精度要求太高,这个方案舍弃
考虑:对于一种区间划分,我怎样对原序列排序能使得代价最小?
你想啊,前面的系数小,后面的系数大,当然是从大到小排个序了!
于是这题就做完了:
先从大到小排序,然后
DP
D
P
代码
//贪心+dp
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
#define maxn 110
#define cl(x,y) memset(x,y,sizeof(x))
#define inf 0x3f3f3f3f
using namespace std;
int f[maxn][maxn], N, u[maxn], W, U;
int read(int x=0)
{
char c, f=1;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-48;
return f*x;
}
bool cmp(int a, int b){return a>b;}
void init()
{
int i;
N=read(), W=read();
for(i=1,U=0;i<=N;i++)u[i]=read(), U+=u[i];
sort(u+1,u+N+1,cmp);
for(i=1;i<=N;i++)u[i]+=u[i-1];
cl(f,inf);
}
void dp()
{
int i, j, k;
f[0][0]=0;
for(i=1;i<=N;i++)for(j=1;j<=W;j++)for(k=0;k<i;k++)f[i][j]=min(f[i][j],f[k][j-1]+i*(u[i]-u[k]));
printf("%.4lf\n",(double)f[N][W]/U);
}
int main()
{
int T=read();
while(T--)init(), dp();
return 0;
}