• HDU4726——Kia's Calculation——2013 ACM/ICPC Asia Regional Online —— Warmup2


    题目的意思是给你两个数字(多达10^6位)

    做加法,但是有一点,没有进位(进位不算,相当于这一位相加后对10取模)

    你可以任意排列两个数字中的每一位,但是不能是0开头。

    现在题目要求以这种不进位的算法计算得到的最大值是多少?

    看完题目就会知道,这个题目一定不是dp,或者说根本不是什么高端的算法,那是什么呢?

    对,你没有猜错——贪心。

    为什么可以用贪心呢?我们比较一个数,都是从高位的先比较,所以只要高位的大,这个数就大;于是我们要得到最大的和,就要先统计能够构成多少个9,多少个8……(有大的就先构成大的!)。

    但是考虑到首位不能是0这个特殊条件,我们需要对首位进行精心的选择。怎么选?首先看能够用两位废0数构成9,如果不行,那就构成8,7……

    知道找到能够用两个非0数构成的最大数为止,这个数就是最前的(最高位)。

    最最容易错的一点就是你怎么得到你构成的取法是最优的呢?

    我们除了要得到一个最大的首位以外,还要考虑我们得到了这个数字后会影响我们获得其他的数字吗?

    所以我们除了要得到一个最大的首位以外,还要保证这个首位相同的情况下,对构成其他的数字的影响最小(我个人的想法,应该是对的,觉得有为题的可以加我QQ:121462136仔细探讨一下)。

    后面的就是赤裸裸的贪心了。

    我的代码:(A了是A了,我觉得也可能是题目的数据有点弱呢,我觉得我这个方法也不是很对的赶脚,诶,不好说,欢迎联系我指出错误)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define maxn 1000100
    using namespace std;
    
    char s[maxn];
    int a[3][10],t,lead,tep,num[10],tot,cas=0;
    
    int count1(int A)//考虑第一个数字取A,影响能构成的一个数字并返回
    {
        for (int i=9; i>0; i--)
        {
            int B=(10+i-A)%10;
            if (a[1][A] && a[2][B]) return i;
        }
        return 0;
    }
    
    int count2(int B)//与上同理,只是第二组取B
    {
        for (int i=9; i>0; i--)
        {
            int A=(10+i-B)%10;
            if (a[1][A]&& a[2][B]) return i;
        }
        return 0;
    }
    
    void getlead()//找首位数字
    {
        int affect=10,A=0;
        for (int a1=1; a1<=9; a1++)
        {
            int a2=(10+9-a1)%10;
            if (a1*a2 && a[1][a1] && a[2][a2])
            {
                lead=9;
                a[1][a1]--,a[2][a2]--;
                return;
            }
        }//如果是9,不用考虑对别的影响(想想为什么?)
        for (lead=8; lead>0; lead--)
        {
            for (int a1=1; a1<=9; a1++)
            {
                int a2=(10+lead-a1)%10;
                if (a1*a2 && a[1][a1] && a[2][a2])
                {
                    if (max(count1(a1),count2(a2))<affect) affect=max(count1(a1),count2(a2)),A=a1;//取影响的数字最小的。
                }
            }
            if (A>0)//找到非0的首位了,上下都要减去一个作为更新
            {
                a[1][A]--;
                a[2][(10+lead-A)%10]--;
                return;
            }
        }
    }
    
    int main()
    {
        scanf("%d",&t);
        while (t--)
        {
            lead=0;
            memset(a,0,sizeof a);
            memset(num,0,sizeof num);
            scanf("%s",s);   tot=strlen(s);
            for (int i=0; s[i]; i++) a[1][s[i]-'0']++;
            scanf("%s",s);
            for (int i=0; s[i]; i++) a[2][s[i]-'0']++;
            getlead();
            for (int i=9; i>0; i--)//枚举假设构成的数字为i,从大到小嘛,先构成大的。
            {
                for (int a1=0; a1<=9; a1++)
                {
                    int a2=(10+i-a1)%10;
                    tep=min(a[1][a1],a[2][a2]);
                    a[1][a1]-=tep,a[2][a2]-=tep;
                    num[i]+=tep;
                }
            }
            printf("Case #%d: ",++cas);
            if (lead==0)//首位为0的话就说明答案为0
            {
                printf("0
    ");
            }
            else//否则说明答案一定有n位(想想为什么?)
            {
                printf("%d",lead);
                tot--;
                for (int i=9; i>0; i--)
                {
                    for (int j=1; j<=num[i]; j++) printf("%d",i);
                    tot-=num[i];
                }
                for (int i=1; i<=tot; i++) printf("0");
                printf("
    ");
            }
        }
        return 0;
    }
    如有转载,请注明出处(http://www.cnblogs.com/lochan)
  • 相关阅读:
    顺序链表的实现
    交换排序(冒泡排序与快速排序)
    插入排序及升级版希尔排序
    循环队列的顺序结构实现
    .NET资源泄露与处理方案
    获取某月第一天,最后一天的sql server脚本
    Oracle Net Manager 的使用方法(监听的配置方法)
    如果有人问你数据库原理,叫他看这篇文章
    如何统计一段时间内但不包含周六日的所有日期
    一个技术人的知识管理方法论
  • 原文地址:https://www.cnblogs.com/lochan/p/3315956.html
Copyright © 2020-2023  润新知