USACO Party Lamps

1、这是IOI98的一道题,写到1:20。。。其实主要框架在0点就写好了,但是由于没注意二进制数是要排序输出的,所以一直WA。。。

2、还是比较麻烦的,首先要将状态压缩到2的4次方个,其次c小于4时要单独讨论,最后就是对二进制数排序了,其实就是字典序。。。

3、刚开始还看错题了,3、4行输入都是描述最终状态的。。。

/*
ID:mrxy564
PROG:lamps
LANG:C++
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int bstate[110],estate[110],temp[110];
int n,c,cnt=0,tempnum,bit,acnt=0;
char s[110][110];
int count(int num){
    int bit=1,cnt=0;
    for(int i=0;i<4;i++){
     cnt+=bit&num?1:0;
  bit<<=1;
 }
 return cnt;
}
void change(int ope){
    if(ope==1){
     for(int i=1;i<=n;i++)
   temp[i]=temp[i]?0:1;
 }
 if(ope==2){
     for(int i=1;i<=n;i+=2)
      temp[i]=temp[i]?0:1;
 }
 if(ope==3){
     for(int i=2;i<=n;i+=2)
   temp[i]=temp[i]?0:1;
 }
 if(ope==4){
     for(int i=1;i<=n;i++){
      if((i-1)%3==0)
       temp[i]=temp[i]?0:1;
  }
 }
 return;
}
bool equal(int a[],int b[]){
    for(int i=1;i<=n;i++){
  if(b[i]==3&&a[i]!=0) return false;
  if(b[i]==2&&a[i]==0) return false;
 }
 return true;
}
int main(){
 freopen("lamps.in","r",stdin);
 freopen("lamps.out","w",stdout);
    scanf("%d%d",&n,&c);
 for(int i=0;i<110;i++) bstate[i]=1;
 for(int i=0;i<110;i++) estate[i]=1;
 while(1){
    scanf("%d",&tempnum);
    if(tempnum==-1) break;
    else estate[tempnum]=2;
 }
 while(1){
    scanf("%d",&tempnum);
    if(tempnum==-1) break;
    else estate[tempnum]=3;
 }
 bool flag=false;
 for(int i=0;i<16;i++){
      bit=count(i);
      if((bit&1)!=(c&1)||bit>c) continue;
      for(int j=1;j<=n;j++)
       temp[j]=bstate[j];
      if(i&1) change(1);
      if(i&2) change(2);
      if(i&4) change(3);
      if(i&8) change(4);
      if(equal(temp,estate)){
   for(int i=n;i>=1;i--){
       s[acnt][i-1]=temp[i]+'0';
   }
   s[acnt][n+1]='\n';
   acnt++;
   flag=true;
    }
 }
 if(flag){
    for(int i=0;i<acnt-1;i++)
     for(int j=i+1;j<acnt;j++)
   if(strcmp(s[i],s[j])>0){
    char temps[110];
    strcpy(temps,s[i]);
       strcpy(s[i],s[j]);
       strcpy(s[j],temps);
   }
    for(int i=0;i<acnt;i++){
         printf("%s\n",s[i]);
    }
 }
 if(!flag)
    printf("IMPOSSIBLE\n");
 return 0;
}

官方题解:

There are a number of insights required for this problem.

The main insight is that no matter which switches get pressed, the light pattern will repeat every six lamps. Another insight is that the order of switch presses does not matter, and that pressing a switch twice is the same as not pressing a switch at all.

Any even number of switch presses greater than four might as well just be four switch presses, and and any odd number greater than three might as well just be three presses.

We can treat the light information as describing only the first six lamps (by treating lamp n as lamp (n mod 6)), and only try pressing each switch once or not at all.

To maintain the actual information about which lights are on, we use an integer holding six bits. The least significant bit is light 6, the next least is light 5, and so on. This has the effect that treating these bit strings as normal numbers sorts the same way that we need to print them.

Switches are represented as bit vectors that say which lights get toggled.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define MAXLAMP	6
#define LAMPMASK	((1<<MAXLAMP)-1)

int nlamp;
int nswitch;
int ison;
int known;

int poss[1<<MAXLAMP];

int flip[4] = {
    LAMPMASK,		/* flip all lights */
    LAMPMASK & 0xAA, 	/* flip odd lights */
    LAMPMASK & 0x55,	/* flip even lights */
    LAMPMASK & ((1<<(MAXLAMP-1))|(1<<(MAXLAMP-4)))	/* lights 1, 4 */
};

/*
 * Starting with current light state ``lights'', flip exactly n switches
 * with number >= i.
 */
void
search(int lights, int i, int n)
{
    if(n == 0) {
	if((lights & known) == ison)
	    poss[lights] = 1;
	return;
    }

    for(; i<4; i++)
	search(lights ^ flip[i], i+1, n-1);
}

void
printseq(FILE *fout, int lights)
{
    int i;
    char s[100+1];

    for(i=0; i<nlamp; i++)
	s[i] = (lights & (1<<(MAXLAMP-1 - i%MAXLAMP))) ? '1' : '0';
    s[nlamp] = '\0';
    fprintf(fout, "%s\n", s);
}

void
main(void)
{
    FILE *fin, *fout;
    int a, i, impossible;

    fin = fopen("lamps.in", "r");
    fout = fopen("lamps.out", "w");
    assert(fin != NULL && fout != NULL);

    fscanf(fin, "%d %d", &nlamp, &nswitch);

    for(;;) {
	fscanf(fin, "%d", &a);
	if(a == -1)
	    break;
	a = MAXLAMP-1 - (a-1) % MAXLAMP;
	ison |= 1<<a;
	known |= 1<<a;
    }

    for(;;) {
	fscanf(fin, "%d", &a);
	if(a == -1)
	    break;
	a = MAXLAMP-1 - (a-1) % MAXLAMP;
	assert((ison & (1<<a)) == 0);
	known |= 1<<a;
    }

    if(nswitch > 4)
	if(nswitch%2 == 0)
	    nswitch = 4;
	else
	    nswitch = 3;

    for(; nswitch >= 0; nswitch -= 2)
	    search(LAMPMASK, 0, nswitch);

    impossible = 1;
    for(i=0; i<(1<<MAXLAMP); i++) {
	if(poss[i]) {
	    printseq(fout, i);
	    impossible = 0;
	}
    }
    if(impossible)
	fprintf(fout, "IMPOSSIBLE\n");

    exit(0);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值