Solution
考虑贪心。
那么 (s_i=11) 的肯定都选,因为这样不会使答案变劣
重点就是 (10,01,00) 之间怎么选
首先, (00) 肯定是最劣的,所以先考虑 (01,10) 。
有一个结论是:在最优情况, (s_i=01) 全部选出,或者 (s_i=10) 全部选出
证明:设现在选了 (x) 个 (01) 的, (y) 个 (10) 的,那么再选 (min(x,y)) 个 (01) 和 (10) 显然是不会超过限制的。
按照 (s_i=10) 全部选择,然后考虑剩下的 (01,00)
因为要最大,即贪心。所以要按照 (a_i) 排序,然后枚举 (s_i=01) 的选择个数 (j) 。
当 (num_{11}+j<num_{10}) 即支持Bob的人太少,或 (num_{11}+num_{10}<j) 即支持Alice的人太少,就跳过这个 (j)
那么剩下的可选的就只有 (s_i=00) 的,数量为 (min{num_{00},num_{11}+j-num_{10},num_{11}+num_{10}-j}) 个
其中第一个是 (s_i=00) 初始数量,第二个是最多还有多少人能不支持Bob,第三个是最多还有多少人能不支持Alice
而 (s_i=01) 同理即可
而枚举时候的价值可以拿前缀和优化,达到 (O(nlog n)) 的时间复杂度。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+10;
int n,num[5],a[5][N],cnt,sum,ans;
inline bool cmp(const int &a,const int &b){
return a>b;
}
int main(){
scanf("%d",&n);
for(int i=1,x,y;i<=n;i++){
scanf("%d%d",&x,&y);
if(!x) a[0][++num[0]]=y;
else if(x==1) a[1][++num[1]]=y;
else if(x==10) a[2][++num[2]]=y;
else a[3][++num[3]]=y,sum+=y;
}
sort(a[0]+1,a[0]+num[0]+1,cmp);
sort(a[1]+1,a[1]+num[1]+1,cmp);
sort(a[2]+1,a[2]+num[2]+1,cmp);
for(int i=2;i<=num[0];i++) a[0][i]+=a[0][i-1];
for(int i=2;i<=num[1];i++) a[1][i]+=a[1][i-1];
for(int i=2;i<=num[2];i++) a[2][i]+=a[2][i-1];
for(int i=0;i<=num[1];i++){
if(num[3]+i<num[2]||num[3]+num[2]<i) continue;
ans=max(ans,a[1][i]+a[2][num[2]]+a[0][min(num[0],min(num[3]+i-num[2],num[3]+num[2]-i))]);
}
for(int i=0;i<=num[2];i++){
if(num[3]+i<num[1]||num[3]+num[1]<i) continue;
ans=max(ans,a[2][i]+a[1][num[1]]+a[0][min(num[0],min(num[3]+i-num[1],num[3]+num[1]-i))]);
}
printf("%d
",ans+sum);
return 0;
}
我这里把 (00,01,10,11) 看作二进制了,心领神会吧(o゚v゚)ノ