题目链接:
题目大意:
给出一个桌子,有n个腿,每个腿的长度是l,拆掉这条腿的花费是d,当最长的腿占腿总数大于其他腿的总数,那么合法,问如何才能花费最小到达合法情况
题目分析:
突然一看,像是dp,因为是求最优解,但是没有思路,那么我就只能想到枚举最后的最长的那条腿的方法,比这条腿长的腿一定是要拆掉的,所以可以预处理出比长度为i的腿拆掉的总数和总花费,然后在枚举每个i的时候就可以o(1)的获取到大于i的总数和总花费,然后在从比当前腿小的中选花费最小的,首先对所有的腿排序,然后按从小到大枚举的过程中,利用一个数组记录下每个d值下的个数(预处理),然后每次将比它小的添加,这个操作均摊O(n)的复杂度,然后每次需要枚举200值范围内从小到大,直到当前剩余的腿的个数小于最大的腿的个数,总的复杂度是201*O(n),妥妥的能过
代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define MAX 100007
using namespace std;
int d[MAX];
int l[MAX];
int num[207];
int mark[MAX];
int sum[MAX];
int total[MAX];
int n;
struct Node
{
int d;
int l;
bool operator < ( const Node & a ) const
{
return l < a.l;
}
}b[MAX];
int main ( )
{
while ( ~scanf ( "%d" , &n ) )
{
memset ( num , 0 , sizeof ( num ) );
memset ( sum , 0 , sizeof ( sum ) );
memset ( total , 0 , sizeof ( total ) );
memset ( mark , 0 , sizeof ( mark ) );
int low = MAX , up = 0;
for ( int i = 0 ; i < n ; i++ )
{
scanf ( "%d" , &l[i] );
low = min ( low , l[i] );
up = max ( up , l[i] );
}
for ( int i = 0 ; i < n ; i++ )
scanf ( "%d" , &d[i] );
for ( int i = 0 ; i < n ; i++ )
{
mark[l[i]]++;
total[l[i]] += d[i];
}
for ( int i = up ; i >= low ; i-- )
{
sum[i] = sum[i+1] + mark[i];
total[i] += total[i+1];
}
for ( int i = 0 ; i < n ; i++ )
{
b[i].d = d[i];
b[i].l = l[i];
}
sort ( b , b+n );
int cnt = 0;
int ans = 1e9;
int temp;
//cout <<"low-up : " << low << " " << up << endl;
for ( int i = low ; i <= up ; i++ )
{
if ( !mark[i] ) continue;
while ( cnt < n && b[cnt].l < i )
num[b[cnt++].d]++;
temp = total[i+1];
int p = n - sum[i];
//cout << " test : " << i << " " << p << " " << temp << " ";
for ( int j = 1 ; j <= 200 ; j++ )
{
if ( p < mark[i] ) break;
if ( p-mark[i]+1 < num[j] )
{
temp += (p-mark[i]+1)*j;
p -= mark[i]-1;
break;
}
temp += num[j]*j;
p -= num[j];
}
//cout << temp << endl;
ans = min ( ans , temp );
}
printf ( "%d\n" , ans );
}
}