POJ-2011-Primary X-Subfactor Series【位运算】【记忆化搜索】【好题】

来源:互联网 发布:java wait join 编辑:程序博客网 时间:2024/06/05 15:31

Description

Let n be any positive integer. A factor of n is any number that divides evenly into n, without leaving a remainder. For example, 13 is a factor of 52, since 52/13 = 4. A subsequence of n is a number without a leading zero that can be obtained from n by discarding one or more of its digits. For example, 2, 13, 801, 882, and 1324 are subsequences of 8013824, but 214 is not (you can’t rearrange digits), 8334 is not (you can’t have more occurrences of a digit than appear in the original number), 8013824 is not (you must discard at least one digit), and 01 is not (you can’t have a leading zero). A subfactor of n is an integer greater than 1 that is both a factor and a subsequence of n. 8013824 has subfactors 8, 13, and 14. Some numbers do not have a subfactor; for example, 6341 is not divisible by 6, 3, 4, 63, 64, 61, 34, 31, 41, 634, 631, 641, or 341.

An x-subfactor series of n is a decreasing series of integers n1, …, nk, in which (1) n = n1, (2) k >= 1, (3) for all 1 <= i < k, ni+1 is obtained from ni by first discarding the digits of a subfactor of ni, and then discarding leading zeros, if any, and (4) nk has no subfactor. The term “x-subfactor” is meant to suggest that a subfactor gets x’ed, or discarded, as you go from one number to the next. For example, 2004 has two distinct x-subfactor series, the second of which can be obtained in two distinct ways. The highlighted digits show the subfactor that was removed to produce the next number in the series.
2004 4

2004 200 0

2004 200 0

The primary x-subfactor series has maximal length (the largest k possible, using the notation above). If there are two or more maximal-length series, then the one with the smallest second number is primary; if all maximal-length series have the same first and second numbers, then the one with the smallest third number is primary; and so on. Every positive integer has a unique primary x-subfactor series, although it may be possible to obtain it in more than one way, as is the case with 2004.

Input

The input consists of one or more positive integers, each less than one billion, without leading zeroes, and on a line by itself. Following is a line containing only “0” that signals the end of the input.
Output

For each positive integer, output its primary x-subfactor series using the exact format shown in the examples below.

Sample Input

123456789
7
2004
6341
8013824
0

Sample Output

123456789 12345678 1245678 124568 12456 1245 124 12 1
7
2004 200 0
6341
8013824 13824 1324 132 12 1

题目链接:POJ-2011

题目大意:

定义:subfactor:    1.v为u的子串        1)不含前导0        2)不能乱序        3)不能自己添加数字        4)至少删除一个数字    2.v为u的因数:u%v==0    3.v > 1

给出一个数字n(不超过10亿),每次删去一个他的subfactor,直到没有subfactor。
使得删减次数最多。如果存在次数一样则输出字典序最小的那个序列

题目思路:用位运算处理出子集

以下是代码:

#include <iostream>#include <iomanip>#include <fstream>#include <sstream>#include <cmath>#include <cstdio>#include <cstring>#include <cctype>#include <algorithm>#include <functional>#include <numeric>#include <string>#include <set>#include <map>#include <stack>#include <vector>#include <queue>#include <deque>#include <list>using namespace std;#define inf 1<<30#define maxn 3005map <int,int> mp;vector <int> vec[maxn];int dp[maxn];int val[maxn];vector <int> ans;int getnum(int x[], int len, int poi){    int res = 0;    for (int i = len - 1; i >= 0; i--)    {        if (poi & (1 << i)) res = res * 10 + x[i];    }    return res;}bool Leading_Zero(int x[], int len, int poi){    for (int i = len - 1; i >= 0; i--)    {        if (poi & (1 << i))        {            if (x[i] == 0) return true;            return false;        }    }    return true;}int solve(int num){    if (dp[num] != -1) return dp[num];  //记忆化搜索    dp[num] = 0;    int len = vec[num].size();    for(int i = 0; i < len; i++)    {        dp[num] = max(dp[num], solve(vec[num][i]));    }    return ++dp[num];}void dfs(int cur){    ans.push_back(val[cur]);    int len = vec[cur].size();    if (len == 0) return;    int minnum = inf,best = 0;    for (int i = 0; i < len; i++)    {        int v = vec[cur][i];        if (dp[cur] == dp[v] + 1 && val[v] < minnum)        {            minnum = val[v];            best = v;        }    }    dfs(best);}int main(){    int num;    while(cin >> num)    {        if (num == 0) break;        memset(dp, -1, sizeof dp);  //记录当前值为i的时候,深度(后面最多能删减几次)        ans.clear();  //存储答案        mp.clear();        for (int i = 0; i < 3000; i++) vec[i].clear();        if (num < 10) //如果是个位数,无法删,直接输出        {            printf("%d\n",num);            continue;        }        int beginnum = num;        int x[10] = {};        int n = 0;   //记录位数        while(num > 0)        {            x[n++] = num % 10;            num /= 10;        }        int cnt = 0;        for (int i = 1; i < (1 << n); i++)        {            int u = getnum(x, n, i);  //取出x所有子序列            if (!mp[u])            {                mp[u] = ++cnt;  //离散化,标记数字                val[cnt] = u;            }        }        for (int i = 1; i < (1 << n); i++)  //遍历x所有的子集        {            int u = getnum(x, n, i);  //取出该子集的数字            if (Leading_Zero(x,n,i)) continue;            for (int j = (i - 1)&i; j ; j = (j - 1)&i) // 遍历u的所有子集            {                if (Leading_Zero(x,n,j)) continue;                int k = getnum(x, n, j);  //从u中取走的数字                int v = getnum(x,n,i^j); //从u中取走数字后剩下的值                if (k <= 1 || u % k) continue;   //如果u不能整除k,即k不是u的约数                vec[mp[u]].push_back(mp[v]);  //vec[i]表示:当前数字为i的时候,接下来一个取值(满足约数+子串)可以为vec[i][j]            }        }        for(int i = 1; i <= cnt; i++) dp[i] = solve(i);        dfs(mp[beginnum]);        printf("%d",ans[0]);        int len = ans.size();        for (int i = 1; i < len; i++) printf(" %d",ans[i]);        printf("\n");    }    return 0;}

给出另一种写法:

#include <bits/stdc++.h>#define ll long longusing namespace std;ll path[105];ll ans[105];ll pow_10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000 };int anscnt = 0;int getlen(ll x){    int cnt = 0;    while (x){        cnt++;        x /= 10;    }    return cnt;}void dfs(ll x, int cnt){    if (x == 0 || x == 1){        if (cnt<anscnt) return;        else if (cnt>anscnt){            anscnt = cnt;            for (int i = 0; i<anscnt; i++){                ans[i] = path[i];            }        }        else{            int flag = 0;            for (int i = 0; i<cnt; i++){                if (path[i]<ans[i]){                    flag = 1;                    break;                }                else if(path[i] > ans[i]) break;            }            if (flag){                for (int i = 0; i<anscnt; i++){                    ans[i] = path[i];                }            }        }        return;    }    int len = getlen(x);    int end = (1LL << len) - 1;    for (int i = end; i >= 0; i--){        ll res = 0, cut = 0, resct = 0, cutct = 0;        int flag = 0;        for (int j = 0; j<len; j++){            if ((i&(1 << j))>0){                res = x / pow_10[j] % 10 * pow_10[resct++] + res;                flag = 1;            }            else{                cut = x / pow_10[j] % 10 * pow_10[cutct++] + cut;            }        }        if (cut != 0 && cut != 1 && x%cut == 0 && getlen(cut) == cutct){            if (res != 0 || res == 0 && flag == 1){                path[cnt] = res;                dfs(res, cnt + 1);            }            else                dfs(res, cnt);        }    }}int main(){    //freopen("1.in", "r", stdin);freopen("11.out", "w", stdout);    ll a;    while (cin >> a, a){        anscnt = 0;        dfs(a, 0);        printf("%lld", a);        for (int i = 0; i<anscnt; i++){            printf(" %lld", ans[i]);        }        cout << endl;    }    return 0;}
0 0
原创粉丝点击