牛客多校第六场 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",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("\n"); break; }else{ if(!next_permutation(refl,refl+10))break; } } if(flag==0)printf("Case #%d: Impossible\n",I); } return 0; }
PS:蔡勒公式的巧妙之处在于它把1,2月当作去年的13,14月,因此将复杂的置闰放到了年底,简化了计算。