牛客多校第六场 G Is Today Friday? 蔡勒公式/排列

题意:

有一堆日期,这些日期都是星期五,但是数字被映射成了字母A~J,现在让你求逆映射,如果存在多种答案,输出字典序最小的那个。

题解:

用蔡勒公式解决关于星期几的问题。

对于映射,可以用笔者刚刚学会的神器,next_permutation(),直接按照字典序生成排列数作为映射,一旦找到解,就输出,必定是字典序最小的。

理论上,枚举10个排列数需要枚举10!≈3e6次,生成下一个排列复杂度为O(n),每次枚举还要检查1e5个日期。

但是一旦找到合法的映射便可直接输出,而不合法的映射往往在检查前几个日期时就被检查出不合法,因此剪枝效果还是很好的。

注意要对于日期去重,对于去重来说unique()是个神器,能直接在排好序的数组上将重复元素只保留一个,然后返回去重之后的数组的最后一位的后一位的地址。参数也是类似于sort()

#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
string date[100005];
char tmp[20];
//char week[10000][13][32];
int refl[15];
inline bool Leap(int year){
    if(year%400==0||(year%4==0 && year%100!=0))return 1;
    else return 0;
}
inline bool days31(int month){
    return (month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12);
}
inline bool Zeller(int year,int month,int day){
    if(year<1600)return 0;
    if(month==0 || month >=13)return 0;
    if(day<=0)return 0; 
    if(month==2){
        if(Leap(year)){
            if(day>29)return 0;
        }else{
            if(day>28)return 0;
        }
    }
    if(days31(month)){
        if(day>31)return 0;
    }else{
        if(day>30)return 0;
    }
    //判断日期合法性 
    if (month == 1 || month == 2){
        year--;month += 12;
    }
    //判断month是否为1或2 
    int c = year / 100;
    int y = year - c * 100;
    int week = y + y / 4 + c / 4 - 2 * c + 26 * (month + 1) / 10 + day - 1;
    week%=7;
    week+=7;
    week%=7;
    return (week==5);
} 
int main(){
    int T;
    scanf("%d",&T);
    for(int I=1;I<=T;I++){
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%s",tmp);
//            0000/00/00
            tmp[4]=0;
            tmp[7]=0;
            date[i].clear();
            date[i]+=tmp;
            date[i]+=tmp+5;
            date[i]+=tmp+8;
        }
        sort(date+1,date+1+n);
        n=unique(date+1,date+1+n)-date-1;
        //去重 
//        printf("%d
",n);
        for(int i=0;i<10;i++)refl[i]=i;
        bool flag;
        while(1){
            flag=1;
            for(int i=1;i<=n;i++){
                if(Zeller(
                refl[date[i][0]-'A']*1000+
                refl[date[i][1]-'A']*100+
                refl[date[i][2]-'A']*10+
                refl[date[i][3]-'A'],
                refl[date[i][4]-'A']*10+
                refl[date[i][5]-'A'],
                refl[date[i][6]-'A']*10+
                refl[date[i][7]-'A']
                )==0){
                    flag=0;
                    break;
                }
            }
            if(flag==1){
                printf("Case #%d: ",I);
                for(int i=0;i<10;i++)printf("%d",refl[i]);
                printf("
");
                break;
            }else{
                if(!next_permutation(refl,refl+10))break;
            }
        }
        if(flag==0)printf("Case #%d: Impossible
",I);
    }
    return 0;
}

PS:蔡勒公式的巧妙之处在于它把1,2月当作去年的13,14月,因此将复杂的置闰放到了年底,简化了计算。

原文地址:https://www.cnblogs.com/isakovsky/p/11300375.html