传送门:http://codeforces.com/contest/732/problem/E
描述:
题意:
给出n个电脑,m个电源,电脑有一个值ai,电源有一个值bi,电脑和电源能够配对当且仅当ai=bi。有无穷多个适配器,每对电源用一个适配器bi就减少一半(向上取整)。一个电源可以用很多次适配器。求最多配对多少电脑和电源,以及在最多配对下用的最少的适配器。还要输出方案。
思路:
将电源按照从小到大依次尝试和电脑配对,如果能够配对成功就配对。可以反证,假设按照这个顺序配对会使得配对数减少,也就是说有一个比他大的电源本应该和这个电脑相配。因为这两个电源都匹配到了这个电脑,所以当前枚举的电源往下能够匹配的电脑也能被那个比他大的电源匹配掉。
那么这样贪心会不会使得适配器数量不是最佳呢?容易证明是最少的。因为如果有多个值一样的电脑需要匹配当然优选电源小的来匹配(等值的电脑顺序任意),如果本来就只能配对这一个电脑,显然用较小的电源是最优的。
代码:
#include <bits/stdc++.h>
using namespace std;
#define rep(i,k,n) for(int i=k;i<=n;i++)
template<class T> T sqr(T x){ return x * x; }
template<class T> T gcd(T a, T b){ return b ? gcd(b, a%b) : a; }
template<class T> void read(T&num) {
char CH; bool F=false;
for(CH=getchar();CH<'0'||CH>'9';F= CH=='-',CH=getchar());
for(num=0;CH>='0'&&CH<='9';num=num*10+CH-'0',CH=getchar());
F && (num=-num);
}
const int N=2e5+10;
struct node{
int num, id;
node(){}
node(int nu, int i): num(nu), id(i) {}
bool operator < (const node& tmp)const{
if(num != tmp.num)return num < tmp.num;
return id < tmp.id;
}
}p[N], s[N];
std::map<int, int> mp;//计算机值出现的次数
int a[N], b[N], c, u;
int main(){
int n, m;
read(n), read(m);
rep(i, 1, n){
read(p[i].num);
p[i].id = i;
mp[p[i].num]++;
}
sort(p + 1, p + n + 1);
rep(i, 1, m){
read(s[i].num);
s[i].id = i;
}
sort(s + 1, s + m + 1);
rep(i, 1, m){
rep(j, 0, 30){
if(mp.count(s[i].num)){//发现可能匹配
int t = lower_bound(p + 1, p + n + 1, node(s[i].num, 0)) - p +mp[s[i].num] - 1;
//找到位置(由于有可能有多个相同的值,这里先从最后一个开始往前推)
if(p[t].num == s[i].num && !b[p[t].id]){//
b[p[t].id] = s[i].id; //电脑匹配到的socket
mp[s[i].num]--;//数量-1
a[s[i].id] = j; //对应的socket需要多少adapters
u += j;
c++;
break;
}
}
s[i].num = (s[i].num + 1) >> 1;
}
}
printf("%d %d\n", c, u);
rep(i, 1, m)printf("%d%c", a[i], i == m ? '\n':' ');
rep(i, 1, n)printf("%d%c", b[i], i == n ? '\n':' ');
return 0;
}