UVA 1051 Bipartite Numbers(构造)

题面

        The executive officers of the company where you work want to send each other encrypted messages. Rather than use off-the-shelf encryption software such as PGP, they have tasked the IT staff with handling the encryption problem. The IT staff decided on a solution that requires “public” and “private” integer keys. The idea is that everyone can see your public key, but only you know your private key. 
        Your best friend in the company is a wonderful person but a not-so-wonderful programmer. He has created a publicprivate key scheme as follows. A public key can be any positive integer. The corresponding private key is the smallest bipartite number that is greater than and a multiple of the public key. 
        A bipartite number is any positive integer that contains exactly 2 distinct decimal digits s and t such that s is not 0 and all occurrences of s precede all occurrences of t. For example 44444411 is bipartite (s is 4 and t is 1), So are 41, 10000000, and 5555556. However, neither 4444114 nor 44444 are bipartite. 
        Notice that the large bipartite number 88888888888800000 can be nicely described as 12 8’s followed by 5 0’s. You can express any bipartite number using four numbers: m s n t. The numbers s and t are the leading and trailing digits as described above, m is the number of times the digit s appears in the bipartite number, and n is the number of times the digit t appears. 
        The trouble with your friend’s scheme is that it is not too difficult to compute a private key if you know the public key. You need to convince your friend that his public-private key scheme is inadequate before he loses his job over his bad decision! You must write a program that takes public keys as input and displays the corresponding private keys.
Input
The input consists of several test cases. Each test case is on a separate line, and it consists of a single public key in the range 1...99999. The last case is followed by a line containing the integer zero.
Output
For each test case, display a line consisting of the public key, a colon, then 4 integers ‘m s n t’ where m, n, s, and t are as described above.
Sample Input
125 
17502 
2005 
0
Sample Output
125: 1 5 2 0 
17502: 4 7 4 8 
2005: 3 2 3 5

题目链接

UVA_1051

参考链接

VJudge UVA1051 Leaderboord Author: NCTU_Thor 代码

题目简述

将pubic key“翻译”为private key。

public key:any positive integer  [1->99999]

The corresponding private key :the smallest bipartite number that is greater than and a multiple of the public key. 

A bipartite number:例如:33334444,99993333……

private key是最小的,满足两个条件:①是private的倍数②是bipartite number的数

需要以m,s,n,t形式输出private key,private key 由m个s、n个t组成形如sssssttttttttt。

s和t都可能有很多很多个(超过1000),private key远超过long long 的取值范围,

思路

mod_pub(x)函数,返回x对public key取mod的结果。

需要构造出一个形如sssssttttt的数,使它满足两个条件,①是public key的倍数②最小

①是public key的倍数

当s<t时,ssstttt的实际值是可以表示为s*(1111111)+(t-s)*(1111) 4447777==4444444+3333==4*1111111+3*1111

由题意可得 mod_pub(s*(1111111)+(t-s)*(1111))==0,mod_pub(4*1111111+3*1111)==0

即:mod_pub(s*(1111111)-(s-t)*(1111))==0,mod_pub(4*1111111-(-3)*1111)==0

即:mod_pub(s*(1111111))==mod_pub((s-t)*(1111)),mod_pub(4*1111111)==mod_pub((-3)*1111)

当s>t时,ssstttt的实际值可以表示为 s*1111111-(s-t)*1111   7774444==7777777-3333=7*1111111-3*1111

由题意可得mod_pub(s*1111111-(s-t)*1111)==0,mod_pub(7*1111111-3*1111)==0

即mod_pub(s*1111111)==mod_pub((s-t)*1111),mod_pub(7*1111111)==mod_pub(3*1111)

所以无论s<t还是s>t都可以得到mod_pub(s*1111111)==mod_pub((s-t)*1111)的结论。

②最小

m+n==总位数,m+n越小,则private key越小。

m+n相同时,s越小,则private key越小。

m+n相同、s、t 也相同时:

(1)s<t时,n越小,private key越小,如4447<4777

(2)s>t时,n越大,private key越小,如7444<7774

根据以上规则,先写3层循环,从小到大构造形如ssssttt的bipartite number。

/**第一层循环,因为不知道len_pri多大的时候有解,不写结束条件**/
for(int len_pri=2;;len_pri++)/**len_pri==m+n,private key的位数**/
    for(s=1;s<=9;s++)
        for(t=0;t<=9;t++)

 怎样满足在m+n,s,t都相同时,同时满足①public key的倍数和②最小两个条件?(求当前情况下最好的n)

条件①翻译:mod_pub(s*1111111)==mod_pub((s-t)*1111)

如何满足:用s1记录len_pri位的11……11对public key取模后的结果,等式左侧的余数可以计算。

求什么样的n使得,余数==mod_pub((s-t)*1111)

条件②翻译:n最大或者最小

因为不可能在三重循环内,再加一重循环,决策n是否合适,当len_pri不断增加,时间复杂度会很大。

用MAP[(s-t)][余数]来记录当前n<len_pri时,最合适的n,满足余数==mod_pub((s-t)*1111)

n<len_pri,则mod_pub(n个1)是s1的值,所以在一重循环for(int len_pri=2;;len_pri++)对MAP数组进行更新。

因为余数==mod_pub((s-t)*1111)的数不一定比n大,所以要判断构造出的数是否比n大。

程序分析

bool is_finish;是否已经有解
void update_answer()更新答案,因为相同的m+n、s时,构造出数的大小由n、t共同决定,7777779<7888888。
int mod_pub(int x)对public key取模
void init(int pub)初始化MAP数组,len_pri从2开始。则余数==((s-t)*1)%pub的位置(MAP[s-t][余数])要填上1的位数:1
bool judge(int S,int T,int len,int MOD)判断构造出的数是否大于n
void update_MAP(int differ,int len,int remainder/**余数**/)更新MAP数组

程序

#include<stdio.h>
#include<iostream>
using namespace std;
#define maxn 100005
int pub;
int m,s,n,t;/**temporary :m,s,n,t**/
int string_1[]={0,1,11,111,1111,11111};
int len_pub;
int MAP[18][maxn];/**最大||最小n的位数,第二维下标:n个1组成数%pub的余数**/
/**-8<=s-t<=9,0<=s-t+8<=17**/

struct ANS
{
    int m,s,n,t;
}answer;
bool is_finish;
void update_answer()
{
    bool change=false;
    if(m+n<answer.m+answer.n)
        change=true;
    else if(m+n==answer.m+answer.n);
    {
        if(s<answer.s)
            change=true;
        else if(s==answer.s)
        {
            if(m>answer.m&&s<t)
                change=true;
            else if(m<answer.m&&s>t)
                change=true;
            else if(m==answer.m&&answer.t>t)
                change=true;
        }
    }
    if(change)
    {
        answer.m=m;
        answer.n=n;
        answer.s=s;
        answer.t=t;
    }
}

int mod_pub(int x)
{
    if(x>=0)
        return x%pub;
    else
        return ((x%pub)+pub)%pub;
}

void init(int pub)
{
    for(int i=0;i<18;i++)
        for(int j=0;j<=pub;j++)
            MAP[i][j]=0;
    for(int differ=-8;differ<=9;differ++)/**difference=s-t**/
    {
        if(differ==0)
            continue;
        MAP[differ+8][mod_pub(differ)]=1;
    }
    int temp=pub;
    len_pub=0;
    while(temp>0)
    {
        temp/=10;
        len_pub++;
    }
    is_finish=false;
}

bool judge(int S,int T,int len,int MOD)
{
    bool ans=false;
    if(MAP[S-T+8][MOD]==0)
        return false;
    int N=MAP[S-T+8][MOD];
    int M=len-N;
    /**判断数是否>pub**/
    if(M+N>len_pub)
        ans=true;
    else if(S*string_1[M+N]+(T-S)*string_1[N]>pub)
        ans=true;
    if(ans)
    {
        m=M,n=N;
    }
    return ans;
}

void update_MAP(int differ,int len,int remainder/**余数**/)
{
    if(MAP[differ+8][remainder]==0)
    {
        MAP[differ+8][remainder]=len;
        return;
    }
    if(differ>0)/**S>T**/
    {
        MAP[differ+8][remainder]=max(MAP[differ+8][remainder],len);
    }
    else/**S<T**/
    {
        MAP[differ+8][remainder]=min(MAP[differ+8][remainder],len);
    }
}

void work()
{
    for(int len_pri=2,s1=mod_pub(11);;len_pri++,s1=mod_pub(s1*10+1))
    {
        for(s=1;s<=9;s++)
        {
            for(t=0;t<=9;t++)
            {
                if(s==t)
                    continue;
                if(judge(s,t,len_pri,mod_pub(s1*s)))
                {
                    if(!is_finish)
                    {
                        is_finish=true;
                        answer.m=m,answer.n=n;
                        answer.s=s,answer.t=t;
                    }
                    else
                        update_answer();
                }
            }
            if(is_finish)
                return;
        }
        for(int differ=-8;differ<=9;differ++)/**difference=s-t**/
        {
            if(differ==0)
                continue;
            update_MAP(differ,len_pri,mod_pub(s1*differ));
        }
    }
}

int main()
{
    while(scanf("%d",&pub)&&pub)
    {
        init(pub);
        work();
        printf("%d: %d %d %d %d\n",pub,answer.m,answer.s,answer.n,answer.t);
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值