Beyond the Void
BYVoid
USACO 2.2.4 Party Lamps 派对灯

相当繁琐的穷举题,容易超时。我花了大半天才写出来,用了位运算优化和减缩开关次数c才过全了。

先分析算法:穷举每一种按开关的可能。 但计数器C(0<=C<=10000),简单穷举肯定会超时。

根据题上所给的开关规则可以发现任何一个开关按两下等于不按。这一点决定了可以减缩按c的次数。

  • 当(0<=C<=4)时,简单穷举,不改变C。
  • 当(5<=C<=10000)时,穷举C=4,C=3各一次。

分析存储结构: 题 中所给N(10<=N<=100),穷举最大可能性(不优化)就是2^100。用数组1..100存储不方便,因为每次传值都要复制100 次。所以我用了位运算存储:用4个unsigned long int (无符号32位长整型)存储每一位灯开关情况,4*32=128位,最大可以存储 128位,满足最大100位(其实N值还可以更大,越大越能体现位运算的高效性)。

以下运算可以返回二进制a的第从右数第b位数字,要理解。 ((a&(1<<(b-1)))>>(b-1))&1

/*
ID: cmykrgb1
PROG: lamps
LANG: C++
*/
#include <stdio.h>

typedef struct
{
    unsigned long int s[4];
} psi;

FILE *fi,*fo;
int n,c,u;
int k[101],g[101],ck,cg,tch[4],lr;
psi r[20],pmax;
int toc[5];

int gw(psi&amp; p,int w)  //返回p从右数第w位
{
    int k=(w-1)/32;
    w-=32*k;
    return ((p.s[k]&amp;(1&lt;&lt;(w-1)))&gt;&gt;(w-1))&amp;1;
}

void sw(psi&amp; p,int w) //取反p从右数第w位(开关灯)
{
    int k=(w-1)/32;
    if (!gw(p,w))
        p.s[k]+=1&lt;&lt;(w-1-32*k);
    else
        p.s[k]-=1&lt;&lt;(w-1-32*k);
}

void cpy(psi&amp; k,psi&amp; p) //复制数位
{
    int i;
    for (i=0;i&lt;=u;i++)
        k.s[i]=p.s[i];
}

int sm(psi&amp; a,psi&amp; b) //判等
{
    int i;
    for (i=u;i&gt;=0;i--)
        if (a.s[i]!=b.s[i]) return 0;
    return 1;

}

int cun(psi&amp; p) //判重
{
    int i;
    for (i=1;i&lt;=lr;i++)
        if (sm(p,r[i]))
            return 1;
    return 0;
}

void print(psi&amp; p) //输出
{
    int i;
    for (i=1;i&lt;=n;i++)
        putc(gw(p,i)+48,fo);
    putc('n',fo);
}

void check(psi&amp; p) //判断是否符合规则
{
    int j;
    for (j=1;j&lt;=ck;j++)
        if (!gw(p,k[j]))
            return;
    for (j=1;j&lt;=cg;j++)
        if (gw(p,g[j]))
            return;
    if (!cun(p))
    {
        cpy(r[++lr],p);
    }
}

void push(psi p,int c) //穷举
{
    if (c==0)
    {
        check(p);
        return;
    }
    int i;
    psi tp;
    //变换1
    if (toc[1]&lt;2)
    {
        for (i=0;i&lt;=u;i++) tp.s[i]=p.s[i]; //reset tp

        for (i=0;i&lt;=u;i++)
            tp.s[i]=-tp.s[i]-1;
        toc[1]++;
        push(tp,c-1);
        toc[1]--;
    }
    //变换2
    if (toc[2]&lt;2)
    {
        for (i=0;i&lt;=u;i++) tp.s[i]=p.s[i]; //reset tp

        for (i=1;i&lt;=n;i+=2)
            sw(tp,i);
        toc[2]++;
        push(tp,c-1);
        toc[2]--;
    }
    //变换3
    if (toc[3]&lt;2)
    {
        for (i=0;i&lt;=u;i++) tp.s[i]=p.s[i]; //reset tp

        for (i=1;i&lt;=n;i+=2)
            sw(tp,i);
        toc[3]++;
        push(tp,c-1);
        toc[3]--;
    }
    //变换4
    if (toc[4]&lt;2)
    {
        for (i=0;i&lt;=u;i++) tp.s[i]=p.s[i]; //reset tp

        for (i=1;i&lt;=n;i+=3)
            sw(tp,i);
        toc[4]++;
        push(tp,c-1);
        toc[4]--;
    }
}

void sp(psi&amp; a,psi &amp;b) //交换
{
    psi c;
    cpy(c,a);
    cpy(a,b);
    cpy(b,c);
}

int small(psi&amp; a,psi &amp;b) //比较大小
{
    int i;
    int ka,kb;
    for (i=1;i&lt;=n;i++)
    {
        ka=gw(a,i);
        kb=gw(b,i);
        if (ka<kb)>
            return 1;
        else
            if (kb<ka)>
                return 0;
    }
}

void st(void) //排序
{
    int i,j,k;
    psi rmin;
    for (i=1;i&lt;=lr-1;i++)
    {
        cpy(rmin,pmax);
        for (j=i+1;j&lt;=lr;j++)
        {
            if (small(r[j],rmin))
            {
                cpy(rmin,r[j]);
                k=j;
            }
        }
        if (small(r[k],r[i]))
            sp(r[i],r[k]);
    }
}

int main(void) //主函数
{
    int i;
    fi=fopen("lamps.in","r");
    fo=fopen("lamps.out","w");
    fscanf(fi,"%dn%dn",&amp;n,&amp;c);
    u=(n-1)/32;
    do
    {
        fscanf(fi,"%d",&amp;k[++ck]);
    }
    while (k[ck]!=-1);
    ck--;
    do
    {
        fscanf(fi,"%d",&amp;g[++cg]);
    }
    while (g[cg]!=-1);
    cg--;
    fclose(fi);
    if (c&gt;4)
    {
        c=4;
        pmax.s[0]=pmax.s[1]=pmax.s[2]=pmax.s[3]=4294967295;
        push(pmax,c);
        c=3;
        pmax.s[0]=pmax.s[1]=pmax.s[2]=pmax.s[3]=4294967295;
        push(pmax,c);
    }
    else
    {
        pmax.s[0]=pmax.s[1]=pmax.s[2]=pmax.s[3]=4294967295;
        push(pmax,c);
    }
    st();
    if (lr==0) fprintf(fo,"IMPOSSIBLEn");
    for (i=1;i&lt;=lr;i++)
        print(r[i]);
    fclose(fo);
    return 0;
}

上次修改时间 2017-02-03

相关日志