【找规律】六边形 纪中集训
这题一眼看穿其找规律本质
然而如何找规律,这是一个问题
此生最恨结论题(找规律算其中一种吧)、构造题
不得不说此题是打表好题,找不到规律果断skip之后回来还是找不到规律,然后为了30%的部分分卑微地画了100个正六边形
考场上的思路:
首先,它每一圈的个数是有规律的:1、6、12、18······
然后,显然同一圈的 i-1 i i+1是相邻的
那么不同圈上的相邻的怎么算呢?
嗯,这是个问题。
大概画了几圈之后发现这个规律非常的隐晦 (其实我根本就没有找到)
这些正六边形有的对着两个,有的对着一个,完全不知道怎么搞
然而OI的有趣之处就在于此(怎么就开始哲学了)
所谓正难则反,既然找每一个六边形的相邻的已生成的六边形有哪些找不到,我们就想想如何通过已有的推哪些六边形是跟现在的相邻的。
我们就把视线转移到了一个个周围一圈6个的这样的图形:
由于我们的思路已经转移到了“我为人人”的境界,所以i-1以内的数我们就不用管了,因为他们自己会推过来
i+1由i拓展得到的 所以一定与i相邻
然后最难搞的就是其它的数
首先,通过观察,可以知道他们一定都是连续的(逆时针绕圈圈)
其次,i=1的情况明显可以打表,当1移动到2的时候,我们会发现,外面那一圈最大的7,在2的那个圈圈里面变成了最小的。
以此类推后面的圈圈,这是第二个重要的结论。
然后就是这些连续的数的个数,显然就是6(一圈的个数)-i-1以内与它相邻的个数-1(第i+1个)
然后就是代码的细节实现问题了:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 10005
#define INF 0x3f3f3f3f
#define LL long long
int cnt[MAXN],mx[MAXN],id[MAXN],tot[6];
bool vis[MAXN][5];
//cnt[i]:i与多少个i-1以内的数相邻 当前点的决策只与他们有关(虽然这个数组只用于统计个数)
//mx[i]:i的那一圈里面最大的数 =mx[i-1]+6/*一圈6个*/-cnt[i]-1
//当前状态要去更新mx[i-1]到mx[i]的vis和cnt
//i+1是由i拓展得到的 所以一定与i相邻
//vis[i][j]表示第i号的颜色j还能不能用 1表示相邻的已用过,不能用
//tot[i]表示i号颜色的种数
void Init()
{
cnt[1]=0,mx[1]=7,id[1]=1,tot[1]=1;
vis[2][1]=vis[3][1]=vis[4][1]=vis[5][1]=vis[6][1]=vis[7][1]=1;
cnt[2]++;cnt[3]++;cnt[4]++;cnt[5]++;cnt[6]++;cnt[7]++;
for(int i=2;i<=10000;i++)
{
mx[i]=min(mx[i-1]+6-cnt[i]-1/*减去第i+1个数*/-1/*从0开始计数*/,10001/*这里要稍微改大一点 刚好是10000在n=10000时就不能过*/);
//有可能本来mx[]是一个很大的数(不妨设为inf) 也就是说,是一个完全达不到的状态 对结果没有任何影响
//但如果取了10000 循环到mx[i-1]-mx[i]且他们都等于10000时,就错误地把10000更新了
//所以这里也应该设置一个达不到的状态 乱更新也没有什么关系 只要保证在10004以内不会RE就可以了
int lt/*least*/=INF;int lid;
for(int j=1;j<=5;j++)
if(!vis[i][j]&<>tot[j])
lt=tot[j],lid=j;
id[i]=lid,tot[lid]++;
for(int j=mx[i-1];j<=mx[i];j++)
vis[j][lid]=1,cnt[j]++;
vis[i+1][lid]=1,cnt[i+1]++;//cnt表示它自己被别人算的 前面的减去第i+1个数是减去它的第i+1个数
}
}
int main()
{
Init();
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
printf("%d\n",id[n]);
}
return 0;
}
转载请注明出处,有疑问欢迎探讨
博主邮箱 2775182058@qq.com