poj2976(二分求解0/1分数规划模型)

来源:互联网 发布:二维数组去除重复 编辑:程序博客网 时间:2024/06/18 18:37
/*translation:给出n场考试,每场考试有总题量,和你答对的题量。现在要求你去掉一些考试项目,使得你的正确率能够达到最大是多少?solution:0/1分数规划,二分解决赤裸裸的0/1分数规划,但是此题不能采用dp,因为a,b数据太大,超时。所以考虑采用二分来枚举可能的最大正确率。然后判断该次枚举的是否可能。这样就转化成了二分确定最大值的问题。现在怎么判断是个问题。因为不能直接用贪心法来根据单次考试的正确率来决策。这样样例中的0/1项在第一次就被筛了。因为总的正确率是根据总的题量和总的答对数来计算的单单根据一次考试是无法得到正确结果。所以考虑有一子集使得能够满足枚举的正确率mid。这样有sum(ai)/sum(bi) >= mid简单变换之后:sum(ai - mid * bi) >= 0。这样对每场考试就能产生一个衡量指标 ai - mid * bi。问题进一步转换成在众多考试项目中选择一个n-k子集,使得它们的衡量指标之和大于等于0.所以只要排序一下,然后从下标k开始知道末尾,相加起来的就是所能选出的最大衡量指标。这样就能进行判断了。note:*:0/1分数规划的二分做法*:注意输出离小数最近的一个整数的做法。date:2016.11.3*/#include <iostream>#include <cstdio>#include <algorithm>using namespace std;const int maxn = 1000 + 10;struct Test{int a, b;} t[maxn];int n, k, sum_up, sum_down;double s[maxn];bool check(double mid){for(int i = 0; i < n; i++)s[i] = (double)t[i].a - mid * t[i].b;sort(s, s + n);double res = 0.0;for(int i = k; i < n; i++)res += s[i];return res >= 0;}int main(){//freopen("in.txt", "r", stdin);    while(~scanf("%d%d", &n, &k)){if(!n && !k)break;sum_up = sum_down = 0;for(int i = 0; i < n; i++)scanf("%d", &t[i].a);for(int i = 0; i < n; i++)scanf("%d", &t[i].b);double pl = 0.0, pr = 1.0;for(int i = 0; i < 100; i++){double mid = (pl + pr) / 2.0;if(check(mid))pl = mid;elsepr = mid;}printf("%d\n", (int)(pr*100 + 0.5));    }    return 0;}

0 0
原创粉丝点击