Nju第五届ACM程序设计竞赛 F题 Is Greed Correct
2007-05-23 12:49 老博客哈 阅读(2041) 评论(2) 收藏 举报Nju第五届ACM程序设计竞赛 F题
Is Greed Correct
Time limit :   2 seconds
Memory limit:  64 megabytes
换零钱问题的典型问法为:
 给定一系列的硬币面值和一个需要找的钱数,要求把这个钱数换成硬币且使用的个数最少。
对于这个问题,贪心法可以找到一个不错的解,但不一定是最优解。贪心法是这样的:假设要找的
钱数是A,每次找出不大于A的最大的面值的硬币,从A里面减去,至到A=0.我们假设我们所考虑的
硬币系统里面总有一个面值为1,那么问题一定有解。
我们的问题是给定一个硬币系统,判断用贪心法来换零钱是否总能得到最优解,即判断是否对任意的A,贪心法得到的硬币组合方案是否总是最少的。
Input
输入的第一行是一个整数T,表示测试数据的组数。下面有T组测试数据。
每组测试数据有两行: 第一行是一个整数M(2 < M <= 100);第二行是M个用空格隔开的整数,表示这个硬币系统中有哪些币值。每个数都不大于10^7,M个整数互不相同,且一定有一个是1.
Output
对于每组测试数据如果贪心法对于这个硬币系统总是最优的,输出"Yes",否则输出"No"(引号不用输出)。
Example
Input                      Output
2                            Yes
6                            No
1 5 10 25 50 100
3
1 3 4
Hint:
第一组数据是贪心最优的,因为我们生活中使用的是硬币系统是贪心最优的。第二组不是,这是一个反例:如果需要组成6,使用贪心算法的组合为6=4+1+1,需要3个硬币,而最优的组合为6=3+3,仅需两个硬币。
    这个题目很有意思,不过刚开始我没想法。后来ufx告诉我是论文题,我小小的郁闷了一下。为什么说这个问题好呢,因为前不久我在hdoj上刚做了一题“发工资咯:)”,地址在:http://acm.hdu.edu.cn/showproblem.php?pid=2021  这个题目里面的硬币系统就是所谓的canonical,也就是用贪心算法去算得到的硬币个数最少,当时我的队友sigh用bfs去做的,可惜tle了。而现在的这个题目不是纯粹的要我们去求最少的硬币个数,而是要我们判断该硬币系统是不是最优的。看了下标程,panda是判断的范围是1~2*C0(假设硬币C已经按从大到小进行排序过了),然后使用贪心策略保存范围内每种钱的最少个数。这个方法我不能完全肯定它的正确性,ufx也没谈到。
直到看完A Polynomial-time Algorithm for the Change-Making Problem这篇论文之后,才霍然开郎,作者深入剖析了这个问题,论文中假设了一个最小反例,并断言该反例必然存在与一个(n^2)大小的集合内,这里的n指的是硬币系统的个数。然后再利用O(n)的算法检测这些反例是否使得在贪心策略下的个数最少。 其中最重要的一条定理我认为是 最小反例w的最优表示与C(i - 1)的贪心表示前j-1位相同,且第j位要大一,后面全0。
    知道了这个结论,就不难写出算法了,下面把官方的标程贴一下:
 #include <cstdio>
#include <cstdio> #include <algorithm>
#include <algorithm>
 int g[128], g1[128];
int g[128], g1[128]; int c[128];
int c[128]; int M, m;
int M, m;
 int solve(int x, int *a)
int solve(int x, int *a) {
{ int ans = 0;
    int ans = 0; for(int i = 0; i < m; i++) {
    for(int i = 0; i < m; i++) { a[i] = x / c[i];
        a[i] = x / c[i]; x %= c[i];
        x %= c[i]; ans += a[i];
        ans += a[i]; }
    } return ans;
    return ans; }
}
 bool compare(const int &a, const int &b)
bool compare(const int &a, const int &b) {
{ return a > b;
    return a > b; }
}
 int main()
int main() {
{ int t, good;
    int t, good; scanf("%d", &t);
    scanf("%d", &t); while(t--) {
    while(t--) { scanf("%d", &m);
        scanf("%d", &m); for(int i = 0; i < m; i++)
        for(int i = 0; i < m; i++) scanf("%d", &c[i]);
            scanf("%d", &c[i]); std::sort(c, c + m, compare);
        std::sort(c, c + m, compare); good = 1;
        good = 1; for(int i = 1; i < m; i++) {
        for(int i = 1; i < m; i++) { solve(c[i - 1] - 1, g);
            solve(c[i - 1] - 1, g); for(int j = i; j < m; j++) {
            for(int j = i; j < m; j++) { int w = 0, v = 0;
                int w = 0, v = 0; for(int k = 0; k < j; k++) {
                for(int k = 0; k < j; k++) { w += g[k] * c[k];
                    w += g[k] * c[k]; v += g[k];
                    v += g[k]; }
                } w += (g[j] + 1) * c[j];
                w += (g[j] + 1) * c[j]; v += g[j] + 1;
                v += g[j] + 1; if(solve(w, g1) > v) {
                if(solve(w, g1) > v) { //    printf("%d %d\n", w, v);
                //    printf("%d %d\n", w, v); good = 0;
                    good = 0; break;
                    break; }
                } }
            } if(!good)
            if(!good) break;
                break; }
        } if(good)
        if(good) printf("Yes\n");
            printf("Yes\n"); else
        else printf("No\n");
            printf("No\n"); }
    } return 0;
    return 0; }
}
 
                    
                

 
     
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号