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& p,int w) //返回p从右数第w位
{
int k=(w-1)/32;
w-=32*k;
return ((p.s[k]&(1<<(w-1)))>>(w-1))&1;
}
void sw(psi& p,int w) //取反p从右数第w位(开关灯)
{
int k=(w-1)/32;
if (!gw(p,w))
p.s[k]+=1<<(w-1-32*k);
else
p.s[k]-=1<<(w-1-32*k);
}
void cpy(psi& k,psi& p) //复制数位
{
int i;
for (i=0;i<=u;i++)
k.s[i]=p.s[i];
}
int sm(psi& a,psi& b) //判等
{
int i;
for (i=u;i>=0;i--)
if (a.s[i]!=b.s[i]) return 0;
return 1;
}
int cun(psi& p) //判重
{
int i;
for (i=1;i<=lr;i++)
if (sm(p,r[i]))
return 1;
return 0;
}
void print(psi& p) //输出
{
int i;
for (i=1;i<=n;i++)
putc(gw(p,i)+48,fo);
putc('n',fo);
}
void check(psi& p) //判断是否符合规则
{
int j;
for (j=1;j<=ck;j++)
if (!gw(p,k[j]))
return;
for (j=1;j<=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]<2)
{
for (i=0;i<=u;i++) tp.s[i]=p.s[i]; //reset tp
for (i=0;i<=u;i++)
tp.s[i]=-tp.s[i]-1;
toc[1]++;
push(tp,c-1);
toc[1]--;
}
//变换2
if (toc[2]<2)
{
for (i=0;i<=u;i++) tp.s[i]=p.s[i]; //reset tp
for (i=1;i<=n;i+=2)
sw(tp,i);
toc[2]++;
push(tp,c-1);
toc[2]--;
}
//变换3
if (toc[3]<2)
{
for (i=0;i<=u;i++) tp.s[i]=p.s[i]; //reset tp
for (i=1;i<=n;i+=2)
sw(tp,i);
toc[3]++;
push(tp,c-1);
toc[3]--;
}
//变换4
if (toc[4]<2)
{
for (i=0;i<=u;i++) tp.s[i]=p.s[i]; //reset tp
for (i=1;i<=n;i+=3)
sw(tp,i);
toc[4]++;
push(tp,c-1);
toc[4]--;
}
}
void sp(psi& a,psi &b) //交换
{
psi c;
cpy(c,a);
cpy(a,b);
cpy(b,c);
}
int small(psi& a,psi &b) //比较大小
{
int i;
int ka,kb;
for (i=1;i<=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<=lr-1;i++)
{
cpy(rmin,pmax);
for (j=i+1;j<=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",&n,&c);
u=(n-1)/32;
do
{
fscanf(fi,"%d",&k[++ck]);
}
while (k[ck]!=-1);
ck--;
do
{
fscanf(fi,"%d",&g[++cg]);
}
while (g[cg]!=-1);
cg--;
fclose(fi);
if (c>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<=lr;i++)
print(r[i]);
fclose(fo);
return 0;
}
上次修改时间 2017-02-03