Bootstrap

派对灯 Party Lamps [USACO 2.2]

 有的时候,思路远比代码更重要!!!

题目

5. K10855 派对灯 Party Lamps [USACO 2.2]

题目描述

在 IOI98 的节日宴会上,我们有 N(10<=N<=100)盏彩色灯,他们分别从 1 到 N 被标上号码.这些灯都连接到四个按钮:

按钮 1:当按下此按钮,将改变所有的灯的状态:本来亮着的灯就熄灭,本来是关着的灯被点亮.

按钮 2:当按下此按钮,将改变所有奇数号的灯.本来亮着的灯就熄灭,本来是关着的灯被点亮.

按钮 3:当按下此按钮,将改变所有偶数号的灯.本来亮着的灯就熄灭,本来是关着的灯被点亮.

按钮 4:当按下此按钮,将改变所有序号是 3*K+1(K>=0)的灯.例如:1,4,7...(本来亮着的灯就熄灭,本来是关着的灯被点亮.)

一个计数器 C 记录按钮被按下的次数.

当宴会开始,所有的灯都亮着,此时计数器 C 为 0.

你将得到计数器 C(0<=C<=10000)上的数值和经过若干操作后某些灯的状态.写一个程序去找出所有灯最后可能的与所给出信息相符的状态,并且没有重复.

输入格式

不会有灯会在输入中出现两次.

第一行: N.(10<=N<=100)

第二行: C 最后显示的数值.(0<=C<=10000)

第三行: 最后若干亮着的灯的编号,用一个空格分开,以-1 为结束.

第四行: 最后若干关着的灯的编号,用一个空格分开,以-1 为结束.

输出格式

每一行是所有灯可能的最后状态(没有重复).

每一行有 N 个字符,第 1 个字符表示 1 号灯,最后一个字符表示 N 号灯。0 表示关闭,1 表示亮着.这些行必须从小到大排列(看作是二进制数).

如果没有可能的状态,则输出一行'IMPOSSIBLE'.

输入输出样例
输入样例1:复制
10
1
-1
7 -1
输出样例1:复制
0000000000
0101010101
0110110110
说明
【样例说明】

在这个样例中,有三种可能的状态:

一、所有灯都关着

二、1,4,7,10号灯关着,2,3,5,6,8,9亮着。

三、1,3,5,7,9号灯关着,2, 4, 6, 8, 10亮着。

【耗时限制】1000ms 【内存限制】128MB


 思路

 解这道题思路很重要。

想法1:枚举每次按下按键

 每一次按下按键,要么是按下了按键1,要么是按键2,要么是按键3,要么是按键4。

可以写出下面的伪代码:

for(i = 1---->4){//第一次按下的按键

        for(j = 1------>4){//第二次

                for(k = 1-------->4){//第三次

                        .

                        .

                        .

                        for(x = 1-------->4){//第C次

                                算出按完后的状态

                                判断状态是否合法

                                合法的存入set中去//不知道set的去“这里

                        }

                        .

                        .

                        .

                }

        }

}


一共C层循环,每层4次一共是4^C,0<=C<=10000,4的一万次方???

。。。。。。

死的真惨!

pass

 想法2:枚举所有灯按过C次后的状态

 C太big了,n可就小得多了,枚举所有灯按过C次后的状态,再补过程行不行?

伪代码实现一下:

long long T[2^100+5][100+5];

for(i1 = 0------->1){

        for(i2 = 0------->1){

                for(i3 = 0------->1){

                        for(i4 = 0------->1){

                                .

                                .

                                .

                                for(in = 0------->1){

                                        T[i]={i1,i2,i3,i4,...,in};

                                }

                        }

                }

        }

}

判断T[i]是否合法以及是否可以实现。


2^100 TLE超时

2^100*100 MLE超空间

呃。。。

死得更惨呜呜呜。。。

pass

 最终答案:分析一下

 先来康康每个按钮按下啊造成的影响:

1按钮:

灯的编号  :1   2   3   4   5

原来         :1   1   1   1   1

第1次按下:0   0   0   0   0

第2次按下:1   1   1   1   1

第3次按下:0   0   0   0   0

可以很轻松的发现 第2次按下会还原成原来的 第3次按下和第1次按下一模一样。

也就是说:其实1按钮只有2种状态 按下或不按。

2按钮:

灯的编号  :1   2   3   4   5

原来         :1   1   1   1   1

第1次按下:0   1   0   1   0

第2次按下:1   1   1   1   1

第3次按下:0   1   0   1   0

2按钮也只有2种状态:按下或不按。

3按钮:

灯的编号  :1   2   3   4   5

原来         :1   1   1   1   1

第1次按下:1   0   1   0   1

第2次按下:1   1   1   1   1

第3次按下:1   0   1   0   1

4按钮:

灯的编号  :1   2   3   4   5

原来         :1   1   1   1   1

第1次按下:0   1   1   0   1

第2次按下:1   1   1   1   1

第3次按下:0   1   1   0   1

一模一样

写一下伪代码:

for(LL i=0;i<=1;i++){//1按钮
        for(LL j=0;j<=1;j++){//2按钮
            for(LL k=0;k<=1;k++){//3按钮
                for(LL l=0;l<=1;l++){//4按钮
                    判断是否可以按出。
                    模拟现在的状态。
                    判断是否符合要求。
                    合法的存入set中去。
                }
            }
        }
    }


问题来了,i+j+k+l最大也才4,C可是有一万呢!!!怎么判断是否可以按出?

先来列张表!

 让我们补一下伪代码:

for(LL i=0;i<=1;i++){//1按钮
        for(LL j=0;j<=1;j++){//2按钮
            for(LL k=0;k<=1;k++){//3按钮
                for(LL l=0;l<=1;l++){//4按钮
                   if((i+j+k+l)%2!=c%2||i+j+k+l>c) continue;
                    模拟现在的状态。
                    判断是否符合要求。
                    合法的存入set中去。
                }
            }
        }
    }


下一步:模拟现在的状态。

这一步比较简单,标记一下就行了,注意记得清零。

for(LL x=1;x<=n;x++) f[x]=true;
if(i) for(LL x=1;x<=n;x+=1) f[x]=!f[x];
if(j) for(LL x=1;x<=n;x+=2) f[x]=!f[x];
if(k) for(LL x=2;x<=n;x+=2) f[x]=!f[x];
if(l) for(LL x=1;x<=n;x+=3) f[x]=!f[x];

 那么, 如何判断是否符合要求。

首先,用open[]和close[]标记开关,再依次判断,顺便存一下答案

再用insert()存入set中去。

现在,代码变成了这样:

 scanf("%lld%lld",&n,&c);
    while(cin>>x&&x!=-1) open[x]=true;
    while(cin>>x&&x!=-1) close[x]=true;
    for(LL i=0;i<=1;i++){
        for(LL j=0;j<=1;j++){
            for(LL k=0;k<=1;k++){
                for(LL l=0;l<=1;l++){
                    if((i+j+k+l)%2!=c%2||i+j+k+l>c) continue;
                    for(LL x=1;x<=n;x++) f[x]=true;
                    if(i) for(LL x=1;x<=n;x+=1) f[x]=!f[x];
                    if(j) for(LL x=1;x<=n;x+=2) f[x]=!f[x];
                    if(k) for(LL x=2;x<=n;x+=2) f[x]=!f[x];
                    if(l) for(LL x=1;x<=n;x+=3) f[x]=!f[x];
                    bool ok=true;
                    string ans="";
                    for(LL x=1;x<=n;x++){
                        if(open[x]&&!f[x]||close[x]&&f[x]) ok=false;
                        ans.push_back(f[x]+'0');
                    }
                    if(ok) se.insert(ans);
                }
            }
        }
    }


时间复杂度是O(1600),根本不存在TLE的问题。

完整代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n,c,x;
bool open[110],close[110],f[110];
set<string> se;
set<string>::iterator it;
int main(){
	scanf("%lld%lld",&n,&c);
	while(cin>>x&&x!=-1) open[x]=true;
	while(cin>>x&&x!=-1) close[x]=true;
	for(LL i=0;i<=1;i++){
		for(LL j=0;j<=1;j++){
			for(LL k=0;k<=1;k++){
				for(LL l=0;l<=1;l++){
					if((i+j+k+l)%2!=c%2||i+j+k+l>c) continue;
					for(LL x=1;x<=n;x++) f[x]=true;
					if(i) for(LL x=1;x<=n;x+=1) f[x]=!f[x];
					if(j) for(LL x=1;x<=n;x+=2) f[x]=!f[x];
					if(k) for(LL x=2;x<=n;x+=2) f[x]=!f[x];
					if(l) for(LL x=1;x<=n;x+=3) f[x]=!f[x];
					bool ok=true;
					string ans="";
					for(LL x=1;x<=n;x++){
						if(open[x]&&!f[x]||close[x]&&f[x]) ok=false;
						ans.push_back(f[x]+'0');
					}
					if(ok) se.insert(ans);
				}
			}
		}
	}
	if(se.empty()) printf("IMPOSSIBLE");
	else{
		for(it=se.begin();it!=se.end();it++) cout<<*it<<endl;
	}
	return 0;
}

评测记录

 

 创作不易,点个赞再走吧🥰🥰🥰

;