https://www.luogu.org/problemnew/show/P1092
这题如果直接暴力枚举的话,复杂度(26!)=4.0329146112661e+26.
然后用科学的剪枝居然玄学地下降到不足1e+8???
剪枝1:若加数最高位加起来大于n,则剪掉,因为最高位不能进位。
剪枝2:因为对于每一位,来自右一位的进位要么是0,要么是1,所以如果某位(两个加数相加再+1或者+0)%n都不等于和,剪掉。
完全看了洛谷排名第一的题解,他认为靠右边数字大,靠左边数字小,故从右到左摘出来不同的字母,优先从大到小排。我就想,那么靠左数字小,从左到右摘出来不同的字母,优先从小到大排。
结果他AC,我超时一个点,下载数据,发现数据对他有利----最左边的并不那么小,而最右边确实很大,然后我小改数据,本地测就是我的程序秒出,他的过了好几秒。
怎么说呢,数据太弱了,都是靠右边的较大,靠左边的较小,但右边更极端,因此他的思路可以过。
这个程序写起来非常麻烦,变量多,关系复杂,注意的地方很多。
码农题。
#include<bits/stdc++.h>
using namespace std;
int n;
char s[4][30];
int a[30],b[30],c[30];
int p[35],vis[35],used[35],num[35];
void init()
{
scanf("%d",&n);
for(int i=1;i<=3;i++)scanf("%s",s[i]+1);
for(int i=1;i<=n;i++)a[i]=s[1][i]-'A';
for(int i=1;i<=n;i++)b[i]=s[2][i]-'A';
for(int i=1;i<=n;i++)c[i]=s[3][i]-'A';
}
void init2()
{
int tot=0;
for(int i=n;i>=1;i--)
{
if(!vis[a[i]])
{
p[++tot]=a[i];
vis[a[i]]=1;
}
if(!vis[b[i]])
{
p[++tot]=b[i];
vis[b[i]]=1;
}
if(!vis[c[i]])
{
p[++tot]=c[i];
vis[c[i]]=1;
}
}
memset(vis,0,sizeof(vis));
}
bool prune() //剪枝
{
if(used[a[1]]&&used[b[1]]&&num[a[1]]+num[b[1]]>=n) return true;
for(int i=n;i>=1;i--)
{
if(used[a[i]]&&used[b[i]]&&used[c[i]])
if((num[a[i]]+num[b[i]])%n!=num[c[i]]&&(num[a[i]]+num[b[i]]+1)%n!=num[c[i]])
return true;
}
return false;
}
bool check() //是否合法
{
for(int i=n,x=0;i>=1;i--)
{
if((num[a[i]]+num[b[i]]+x)%n!=num[c[i]])return false;
x=(num[a[i]]+num[b[i]]+x)/n;
}
return true;
}
bool dfs(int cur) //现在应该给p数组第cur个元素找真实数字
{
if(prune())return false;
if(cur==n+1&&check())return true;
for(int i=n-1;i>=0;i--) //对于p数组第cur个元素,从大到小代入真实数字n-1~0
{
if(!vis[i])
{
vis[i]=1; //某代入的数字是否用过
used[p[cur]]=1; //代表字母的数字是否已被真实数字代表
num[p[cur]]=i; // 代表字母的数字真实为i
if(dfs(cur+1))return true;
used[p[cur]]=0;
vis[i]=0;
}
}
return false;
}
void print()
{
for(int i=0;i<n;i++)printf("%d ",num[i]);
putchar('\n');
}
void debug()
{
//for(int i=1;i<=4;i++)printf("%d ",p[i]);
//for(int i=1;i<=n;i++)printf("%d %d %d\n",a[i],b[i],c[i]);
}
int main()
{
freopen("input.in","r",stdin);
init(); //字母变数字
init2(); //一般右边的要进位,较大,左边较小,故靠右的优先从大到小枚举,以加速
dfs(1);
print();
//debug();
return 0;
}