POJ Snowflake Snow Snowflakes 3349 (最小表示法查同)

96 篇文章 0 订阅
34 篇文章 0 订阅

这道题用哈希做了好久,用哈希函数做的时候WA,用哈希表做的时候超时,最后用最小表示法查同的方法来做。


最小表示法:


从一个序列中找出以不同元素为起点组成的新序列中的字典序最小序列。


设i=0,j=1,k=0;

比较a[i+k]与a[j+k]的大小;

当a[i+k]==a[j+k]时 k++;

当a[i+k]>a[j+k]时,i=i+k+1; 因为从i->i+k开始到i+k组成的前缀必定小于某个t开始到组成的前缀.

证明;

即证 1.从i->i+k开始到i+k组成的前缀不可能是最小表示

        2.从i->i+k开始到i+k组成的前缀小于从j->j+k开始到j+k组成的前缀

        若2成立,证明1也成立

        所以证明2:

        任选i', i<=i'<=i+k+1, j<=j'<=j+k+1,显然从i'->i'+k开始到i'+k组成的前缀小于从j'->j'+k开始到j'+k组成的前缀

       所以2得证

       所以从i->i+k开始到i+k组成的前缀必定小于某个t开始到组成的前缀.


当a[i+k]<a[j+k]时,j=j+k+1;证明同上略


当i==len时返回i,j中小于len的那个值,就是最小表示的起点.另外当i=j时,移动的那个指针需要+1;

可以有更好的优化,当需要指针i,j滑动时,如果i+k+1小于j,可以让i直接跳到j+1,因为已证明j到j+k的前缀比之前i到i+k的前缀都要小.

最小表示法代码

int MinimumRepresentation(int *a, int len)
{
   int i, j, k=0;
    i=0;j=1;
   while(i<len && j<len)
    {
        k=0;
        while(k<len && a[i+k]==a[j+k])k++;
        if(k==len)return i;
        if(a[i+k]<a[j+k])
        {
         if(j+k+1<=i)j=i+1;
         else j=j+k+1;
        }
       else
        {
            if(i+k+1<=j)i=j+1;
            else i=i+k+1;
        }

    }
    if(i>=len)return j;
    else return i;
}



题解代码:


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int MinimumRepresentation(int *a, int len)
{
   int i, j, k=0;
    i=0;j=1;
   while(i<len && j<len)
    {
        k=0;
        while(k<len && a[i+k]==a[j+k])k++;
        if(k==len)return i;
        if(a[i+k]<a[j+k])
        {
         if(j+k+1<=i)j=i+1;
         else j=j+k+1;
        }
       else
        {
            if(i+k+1<=j)i=j+1;
            else i=i+k+1;
        }

    }
    if(i>=len)return j;
    else return i;
}
struct p
{
    int b[7];
}a[101234];
int cmp(p a, p b) // 根据字典序排序
{
    int i=0;
    while(i< 6 && a.b[i]==b.b[i])i++;
    return a.b[i]<b.b[i];
}
int cmp2(p a, p b)  //比较是否相同
{
    int i=0;
    while(i<6 && a.b[i]==b.b[i])i++;
    if(i==6)return 1;
    else  return 0;
}
int main()
{
    int n;
    scanf("%d", &n);
    int i, j;
    int c[7];
    int d[7];
    int k, e=0;
    for(i=0; i<n; i++)
    {
        for(j=0; j<6; j++)
        {
            scanf("%d", &c[j]);
            d[5-j]=c[j];
        }
        j=MinimumRepresentation(c,6);

        for(k=0; k<6; k++)
        {
            a[e].b[k]=c[j];
            j++;
            if(j==6)j=0;
        }
        e++;
        j=MinimumRepresentation(d, 6);
        for(k=0; k<6; k++)
        {
            a[e].b[k]=d[j];
            j++;
            if(j==6)j=0;
        }
        if(cmp(a[e-1], a[e]));
        else
        {
            for(k=0; k<6; k++)
            {
                a[e-1].b[k]=a[e].b[k];
            }
        }
    }
    sort(a, a+e, cmp);
    for(i=0; i<e-1; i++)
    {
       if(cmp2(a[i], a[i+1]))break;  //相同则退出.
    }
   if(i<e-1)printf("Twin snowflakes found.\n");
    else printf("No two snowflakes are alike.\n");
    return 0;
}


                   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值