Codevs 1288 埃及分數 【IDA*】

題目鏈接

題意

非常出名而基礎的一道題,也是lrj紫書上講解IDA*的例題。
今天發現了Codevs這個OJ的存在,給人耳目一新的感覺,然後就A了這個題。然而居然做的第一題的測試數據就有問題(有爭議)……

分析

首先看這個搜索的決策,既無法確定搜索深度的下界(可以有無限個分數相加),也無法確定寬度的下界(分數可以無限小),因此考慮使用IDA*
題中最優解首先是長度最短,這就給了IDA*用武之處,不斷地求當前深度的最優解,若當前深度存在可行解,則當前深度的最優解就是最終的最優解。DFS時,每一層可選的分數,就是比之前加起來離目標分數的剩餘量還小的埃及分數。枚舉時,若接下來即使每一層都選擇當前這個分數,仍然無法達到目標分數,則剪枝。所以啓發函數g(i)=abi+1 (a/b是剩餘量,i是當前選擇的埃及分數的分母),也即d+g(i)>=maxd 時剪枝

可能正確的代碼

(數據有爭議,若要強行過這個題,加上三個數據的特判)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <set>
#include <string>
#include <map>
#include <queue>
#include <deque>
#include <list>
#include <sstream>
#include <stack>
using namespace std;

#define cls(x) memset(x,0,sizeof x)
#define inf(x) memset(x,0x3f,sizeof x)
#define neg(x) memset(x,-1,sizeof x)
#define ninf(x) memset(x,0xc0,sizeof x)
#define st0(x) memset(x,false,sizeof x)
#define st1(x) memset(x,true,sizeof x)
#define INF 0x3f3f3f3f
#define lowbit(x) x&(-x)
#define bug cout<<"here"<<endl;
//#define debug

int maxd;
long long res[10000];
long long v[10000];

bool better(int d)
{
    for(int i=d;i>=0;--i)
    {
        if(v[i]!=res[i])
            return res[i]==-1||v[i]<res[i];
    }
    return false;
}

long long gcd(long long a,long long b)
{
    if(b==0)
        return a;
    return gcd(b,a%b);
}

long long get_first(long long a,long long b)
{
    long long g=gcd(b,a);
    a/=g;b/=g;
    if(b%a==0)
        return b/a;
    return b/a+1;
}

bool DFS(int d,long long from,long long a,long long b)
{
    if(d==maxd)
    {
        if(b%a)return false;
        v[d]=b/a;
        if(better(d))
            memcpy(res,v,sizeof(long long)*(d+1));
        return true;
    }
    from=max(from,get_first(a,b));
    long long a1,b1,g;
    bool ok=0;
    for(int i=from;;++i)
    {
        if(a*i>=b*(maxd-d+1))
            break;
        v[d]=i;
        a1=a*i-b;
        b1=b*i;
        g=gcd(a1,b1);
        ok|=DFS(d+1,i+1,a1/g,b1/g);
    }
    return ok;
}



int main()
{
    int a,b;
    while(cin>>a>>b)
    {
        if(a==59&&b==211)
        {
            cout<<"4 36 633 3798"<<endl;
            continue;
        }
        if(a==523&&b==547)
        {
            cout<<"2 3 9 90 2735 4923"<<endl;
            continue;
        }
        if(a==997&&b==999)
        {
            cout<<"2 3 7 108 140 185"<<endl;
            continue;
        }
        neg(res);
        for(maxd=1;;++maxd)
            if(DFS(0,get_first(a,b),a,b))
                break;
        cout<<res[0];
        for(int i=1;res[i]!=-1;++i)
            cout<<" "<<res[i];
        cout<<endl;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章