选举(Let's Win the Election)题解
考场上写的纯贪心,假了,只拿了 \(10 pts\) 分。
显然,对于每个州,演讲时间只有 \(0\),\(a_{i}\),\(b_{i}\) 三种情况。
定义演讲时间为 \(a_{i}\) 的州为 \(A\) 类州(即只获得选票),演讲时间为 \(b_{i}\) 的州为 \(B\) 类州(即获得协作者),演讲时间为 \(0\) 的州为 \(C\) 类州(即不去演讲)。
先去演讲 \(B\) 类州,再去演讲 \(A\) 类州是更优的(人多力量大嘛)。
所以我们按照 \(b_{i}\) 排序。
我的贪心假的原因是:
贪心选择 \(b_{i}\) 最小的州成为 \(B\) 类州,枚举 \(B\) 类州的个数,然后选择剩下的州中 \(a_{i}\) 最小的州成为 \(A\) 类州
这个时候就会有一个问题,在两个州 \(x\),\(y\) 中,\(x\) 的 \(b_{i}\) 和 \(a_{i}\) 都比 \(y\) 的小时,可能选择 \(y\) 比选择 \(x\) 更优,所以贪心就假了。
我们枚举 \(b_{i}\) ,进行 \(dp\)。
由于是按照 \(b_{i}\) 升序排列的,那么如果 \(B\) 类州之前存在 \(C\) 类州,让该 \(C\) 类州成为 \(B\) 类州,该 \(B\) 类州成为 \(C\) 类州是更优的。
那么序列的前半部分只有 \(A\) 类州或 \(B\) 类州,后半部分只有 \(A\) 类州或 \(C\) 类州。
所以定义 \(dp_{i,j}\) 表示枚举到第 \(i\) 个州,选了 \(j\) 个 \(B\) 类州的最少时间。
枚举 \(B\) 类州的个数 \(sum\),然后进行 \(dp\)。
\(dp\) 到 \(sum\) 后停止,计算剩下的州中最小的 \(A\) 类州的时间。
设后 \(x\) 个州里,\(a_{i}\) 最小的 \(y\) 个州的 \(a_{i}\) 之和为 \(w(x,y)\)。
那么答案就是 \(min_{i = sum}^{k}dp_{i,sum} + w(n - i,k - i)\)。
\(dp\) 的时间复杂度是 \(\mathcal{O}(n^{2})\),由于要枚举 \(B\) 类州的个数,所以总时间复杂度 \(\mathcal{O}(n^{3})\)。
#define Kamisato_Ayaka no tui,juan
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define ll long long
#define db double
inline int in(){
int x = 0;
bool f = 0;
char c = getchar();
while(c > '9' || c < '0'){
if(c == '-') f = 1;
c = getchar();
}
while(c <= '9' && c >= '0'){
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
if(f) return -x;
else return x;
}
const int N = 510;
int n,k;
int a[N];
db ans = 1e9;
db dp[N][N];
struct node{
int a,b;
}q[N];
bool operator < (const node x,const node y){
return x.b < y.b;
}
inline void work(int x){
for(int i = 1;i <= n;++i) a[i] = q[i].a;
for(int i = 0;i <= n;++i)
for(int j = 0;j <= n;++j) dp[i][j] = 1e9;
dp[0][0] = 0;
db res = 1e9;
for(int i = 1;i <= n;++i){
for(int j = 0;j <= min(x,i);++j){
dp[i][j] = dp[i - 1][j] + q[i].a * 1.0 / (x + 1);
if(j >= 1 && q[i].b != 1e9) dp[i][j] = min(dp[i][j],dp[i - 1][j - 1] + q[i].b * 1.0 / j);
}
}
for(int i = k;i <= n;++i) res = min(res,dp[i][x]);
for(int i = k;i >= x;--i){
sort(a + i + 1,a + n + 1);
db sum = 0;
for(int j = i + 1;j <= k;++j) sum += a[j];
res = min(res,dp[i][x] + sum / (x + 1));
}
ans = min(ans,res);
}
int main(){
// fre(a);
n = in();
k = in();
for(int i = 1;i <= n;++i){
q[i].a = in();
q[i].b = in();
if(q[i].b == -1) q[i].b = 1e9;
}
sort(q + 1,q + n + 1);
for(int i = 0;i <= k;++i) work(i);
printf("%.8lf",ans);
return 0;
}

选举(Let's Win the Election)
浙公网安备 33010602011771号