POJ 2718 Smallest Difference (穷竭搜索)

http://poj.org/problem?id=2718

将一个数切一刀拆成两个数,两个数每一位数字的顺序都可改变,但是不能有前导0。求这两个数之差的最小值。

我使用了搜索并且避免了递归,自认为是比较好的算法。

一开始我想到的是枚举,并且很快给出了实现:

#ifndef ONLINE_JUDGE
#pragma warning(disable : 4996)
#endif
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
 
///SubMain//
int main(int argc, char *argv[])
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    int n;
    cin >> n;
    cin.ignore();
    while (n--)
    {
        string all;
        getline(cin, all);
        all.erase(remove(all.begin(), all.end(), ' '), all.end());
        int result = 0x3F3F3F3F;
        int cut = all.size() / 2;
        do 
        {
            string s1 = all.substr(0, cut);
            string s2 = all.substr(cut);
            if ((s1[0] == '0' && s1.size() > 1) ||
                (s2[0] == '0' && s2.size() > 1)
                )
            {
                continue;
            }
            int n1 = atoi(s1.c_str());
            int n2 = atoi(s2.c_str());
            int dif = abs(n1 - n2);
            if (dif < result)
            {
                result = dif;
            }
        } while (next_permutation(all.begin(), all.end()));
         
        cout << result << endl;
    }
#ifndef ONLINE_JUDGE
    fclose(stdin);
    fclose(stdout);
    system("out.txt");
#endif
    return 0;
}
///End Sub//

没料到TLE,后来想了想,这里的复杂度是O((length!)n),当length = 10 的时候内层最高达到了3628800 次循环,而用例则可能是很多的。

要优化得下点功夫,起先想到改用模拟的方法,分奇偶讨论,可是这道题目是放在2.1 最基础的“穷竭搜索” 穷竭搜索这节里,目的是训练搜索算法。

于是继续走搜索的路线,上面的程序之所以慢,是因为重复计算。比如第一个字串为12,第二个字串为34这种情况和第一个字串为34,第二个字串为12这种情况被视作不同的情况。这是应当被优化的第一个点。

第二个点是string类型的全排操作比较费时,可以用位运算优化。

我看到有些人用dfs递归,感觉很别扭,代码一不好理解,二浪费栈。

我使用了我最喜欢的玩具bitset:

#ifndef ONLINE_JUDGE
#pragma warning(disable : 4996)
#endif
#include <iostream>
#include <string>
#include <algorithm>
#include <bitset>
using namespace std;
 
///SubMain//
int main(int argc, char *argv[])
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    int n;
    cin >> n;
    cin.ignore();
    while (n--)
    {
        string all;
        getline(cin, all);
        all.erase(remove(all.begin(), all.end(), ' '), all.end());
        int length = all.size();
        int cut = length / 2;
        int permute = 1 << length;
        int result = 0x3F3F3F3F;
        do
        {
            bitset<10> used = static_cast<bitset<10>>(permute);
            string s1, s2;
            for (int i = 0; i < length; ++i)
            {
                if (used[i])
                {
                    s1 += all[i];
                }
                else
                {
                    s2 += all[i];
                }
            }
            if (s1.size() != cut)
            {
                continue;
            }
            if (s1[0] == '0' && s1.size() > 1)
            {
                continue;
            }
            // s1 s2 已经被切割出来了
            // 穷举它们
            do
            {
                int n1 = atoi(s1.c_str());
                do
                {
                    if (s2[0] == '0' && s2.size() > 1)
                    {
                        continue;
                    }
                    int n2 = atoi(s2.c_str());
                    int dif = abs(n1 - n2);
                    //cout << s1 << ' ' << s2 << " dif " << dif << " result: " << result << endl;
                    if (dif < result)
                    {
                        result = dif;
                    }
                } while (next_permutation(s2.begin(), s2.end()));
            } while (next_permutation(s1.begin(), s1.end()));
        } while (--permute);
 
        cout << result << endl;
    }
#ifndef ONLINE_JUDGE
    fclose(stdin);
    fclose(stdout);
    system("out.txt");
#endif
    return 0;
}
///End Sub//
不对,不对,大家很难看懂吧,为了这段代码,笔者也学了很多东西,但是还是觉得太繁琐!因此又补了一份代码!

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define INF 0x3f3f3f3f
using namespace std;

int a[15];
int n;

void solve()
{
    while(a[0]==0)
        next_permutation(a,a+n);

    int ans=INF;
    int mid=(n+1)/2;
    do
    {
        if(a[mid])
        {
            int x=a[0],y=a[mid];
            for(int i=1;i<mid;i++)
                x=x*10+a[i];
            for(int i=mid+1;i<n;i++)
                y=y*10+a[i];
            if(ans>abs(x-y))
                ans=abs(x-y);
        }

    }while(next_permutation(a,a+n));
    cout<<ans<<endl;
}

int main()
{
    int T;
    char c;

    scanf("%d",&T);
    getchar();
    while(T--)
    {
        n=0;
        memset(a,0,sizeof(a));

        while((c=getchar())!='\n')
        {
            if(c!=' ')
                a[n++]=c-'0';
        }

        if(n==1)
            printf("%d\n",a[0]);
        else if(n==2)
            printf("%d\n",abs(a[1]-a[0]));
        else
            solve();
    }
    return 0;
}

哈哈,还没吐的话再来一份dfs代码

#include 
#include 
#include 
#include 
using namespace std;

const int MAX_N = 11;
int ans, n;
int a[MAX_N], b[MAX_N];
bool used[MAX_N];

void solve(int aa) {
    int m = 0;
    for (int i = 0; i < n; ++ i) {
        if (!used[i]) {
            b[m++] = a[i];
        }
    }

    do {
        int bb = 0;
        for (int i = 0; i < m; ++ i) {
            bb = bb*10 + b[i];
        }

        if (b[0] != 0 || m == 1) {
            ans = min(ans, abs(aa - bb));
        }
    }while(next_permutation(b, b + m));
}

void dfs(int k, int r) {
    if (k == n/2) {
        solve(r);
    } else {
        for (int i = 0; i < n; ++ i) {
            if (!used[i]) {
                if (a[i] == 0 && k == 0 && n > 3) continue;
                used[i] = true;
                dfs(k+1, r*10+a[i]);
                used[i] = false;
            }
        }
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.in", "r", stdin);
#endif
    int t;
    scanf("%d\n", &t);
    char str[25];

    while(t --) {
        gets(str);
        n = 0;
        for (int i = 0; str[i] != '\0'; ++ i) {
            if(str[i] >= '0' && str[i] <= '9') {
                a[n++] = str[i] - '0';
            }
        }
        memset(used, false, sizeof(used));
        ans = INT_MAX;
        dfs(0, 0);
        printf("%d\n", ans);
    }
    return 0;
} 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值