Party Lamps
Time Limit: 1000MS | Memory Limit: 10000K | |
Description
To brighten up the gala dinner of the IOI'98 we have a set of N coloured lamps numbered from
1 to N. The lamps are connected to four buttons:
button 1 -- when this button is pressed, all the lamps change their state: those that are ON are turned OFF and those that are OFF are turned ON.
button 2 -- changes the state of all the odd numbered lamps.
button 3 -- changes the state of all the even numbered lamps.
button 4 -- changes the state of the lamps whose number is of the form 3K+1 (with K >= 0), i.e., 1,4,7,...
There is a counter C which records the total number of button presses.
When the party starts, all the lamps are ON and the counter C is set to zero.
You are given the value of counter C and information on the final state of some of the lamps. Write a program to determine all the possible final configurations of the N lamps that are consistent with the given information, without repetitions.
1 to N. The lamps are connected to four buttons:
button 1 -- when this button is pressed, all the lamps change their state: those that are ON are turned OFF and those that are OFF are turned ON.
button 2 -- changes the state of all the odd numbered lamps.
button 3 -- changes the state of all the even numbered lamps.
button 4 -- changes the state of the lamps whose number is of the form 3K+1 (with K >= 0), i.e., 1,4,7,...
There is a counter C which records the total number of button presses.
When the party starts, all the lamps are ON and the counter C is set to zero.
You are given the value of counter C and information on the final state of some of the lamps. Write a program to determine all the possible final configurations of the N lamps that are consistent with the given information, without repetitions.
Input
Your program is to read from standard input. The input contains four lines, describing the number N of lamps available, the number C of button presses, and the state of some of the lamps in the final configuration.
The first line contains the number N and the second line the final value of counter C. The third line lists the lamp numbers you are informed to be ON in the final configuration, separated by one space and terminated by the integer -1. The fourth line lists the lamp numbers you are informed to be OFF in the final configuration, separated by one space and terminated by the integer -1.
The parameters N and C are constrained by:
10 <= N <= 100
1 <= C <= 10000
The number of lamps you are informed to be ON, in the final configuration, is less than or equal to 2.The number of lamps you are informed to be OFF, in the final configuration, is less than or equal to 2.
The first line contains the number N and the second line the final value of counter C. The third line lists the lamp numbers you are informed to be ON in the final configuration, separated by one space and terminated by the integer -1. The fourth line lists the lamp numbers you are informed to be OFF in the final configuration, separated by one space and terminated by the integer -1.
The parameters N and C are constrained by:
10 <= N <= 100
1 <= C <= 10000
The number of lamps you are informed to be ON, in the final configuration, is less than or equal to 2.The number of lamps you are informed to be OFF, in the final configuration, is less than or equal to 2.
Output
Your program is to write to standard output. The output must contain all the possible final configurations (without repetitions) of all the lamps. There is at least one possible final configuration. Each possible configuration must be written on a different line. Each line has N characters, where the first character represents the state of lamp 1 and the last character represents the state of lamp N. A 0 (zero) stands for a lamp that is OFF, and a 1 (one) stands for a lamp that is ON. Configurations should be listed in binary ascending order.
Sample Input
10 1 -1 7 -1
Sample Output
0000000000 0101010101 0110110110
题目大意
对于一串彩灯,提供四种改变彩灯状态(ON<=>OFF)的操作:1.改变所有彩灯状态(亮的变灭,灭的变亮);2.改变奇数彩灯状态;3.改变偶数彩灯状态;4.改变3k+1号彩灯状态(1,4,7,10...)。给定彩灯数目,操作次数,和对于某几个彩灯必须为ON、某几个彩灯必须为OFF的要求,问经过给定次数的操作,最终能达到的满足要求的状态有多少种,输出所有满足要求的彩灯状态。原题中操作次数是1<=C<=10000的。
解题思路
两个办法,第一开始我想的是用简单的dfs,但是单纯的dfs可能会超时,这里可以优化。看了无数博客,一般都是给出找规律的代码,大家发现的规律都不一样,膜拜大神ing,最终找到一个觉得最适合我的最容易理解的,整合了一下我自己的思维 ,分享给大家。
一、找规律枚举法
观察发现1 3 5 2 4 6 这六个灯之后的灯都是根据这些去循环的,也就是循环节长度是6,所以只考虑前6个灯的8种情况
1、为什么循环节是6呢?
我是挨个试的,假设循环节长度是2,那么下述④不符合要求,假设循环节长度是3那么下述②③都不符合,以此类推找到一个最小的符合要求的周期即可
2、为什么是8种情况?
先看4种基本操作,这些都是一步就可以到达的状态
① 000000 按1
② 010101 按2
③ 101010 按3
④ 011011 按4
然后这些基本操作中的一个或几个进行异或(比如①异或②就相当于先按1再按2 )得到的结果再异或,发现只有8种可能
0,0,0,0,0,0,//按1
0,0,1,1,1,0,//按3按4
0,1,0,1,0,1,//按2
0,1,1,0,1,1,//按4
1,0,0,1,0,0,//按1和4
1,0,1,0,1,0,//按3
1,1,0,0,0,1,//按2按4
1,1,1,1,1,1,//不按
0,0,0,0,0,0,//按1
0,0,1,1,1,0,//按3按4
0,1,0,1,0,1,//按2
0,1,1,0,1,1,//按4
1,0,0,1,0,0,//按1和4
1,0,1,0,1,0,//按3
1,1,0,0,0,1,//按2按4
1,1,1,1,1,1,//不按
(上述按法不唯一,比如不按的状态也可以由按1 2 3 来达到)
3、而且发现
按1和按2相当于按3;
按2和按3相当于按1;
按1和按3相当于按2;
按1按2和按3相当于不按;
所以n步能完成的状态,大于n步也能完成。所以只需要遍历一下8个状态符不符合,再判断就是8个状态的最小步数符不符合要求 ,就可以了。
按2和按3相当于按1;
按1和按3相当于按2;
按1按2和按3相当于不按;
所以n步能完成的状态,大于n步也能完成。所以只需要遍历一下8个状态符不符合,再判断就是8个状态的最小步数符不符合要求 ,就可以了。
***这里有一个地方需要说一下,就是按4这种情况(对应上述④状态),按4 可以一步,也可以三步或者三步以上(224)但是就是不能两步达到,所以每个状态对应的最小步数是:1,2,1,3,2,1,2,2
然后贴一下代码 (注释中的代码是打表的方法)
<strong>#include <cstring>
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#define INF 0x3f3f3f3f
const int maxn=5010;
using namespace std;
string ans[9];
int a[110];
int state[9][7]=
{
0,0,0,0,0,0,0,//下标从【1,1】开始
0,0,0,0,0,0,0,//按1
0,0,0,1,1,1,0,//按3按4
0,0,1,0,1,0,1,//按2
0,0,1,1,0,1,1,//按4
0,1,0,0,1,0,0,//按1和4
0,1,0,1,0,1,0,//按3
0,1,1,0,0,0,1,//按2按4
0,1,1,1,1,1,1,//不按
};
//int state[9][101]=
//{
//{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
//{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
//{0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1},
//{0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1},
//{0,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0},
//{0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1},
//{0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0},
//{0,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0},
//{0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
//};
int minn[9]={0,1,2,1,3,2,1,2,2};//8个状态分别按最少的次数
int main()
{
int n,c;
int num=0;
scanf("%d%d",&n,&c);
memset(a,-1,sizeof(a));
int x;
while(scanf("%d",&x) && x!=-1)
a[x]=1;
while(scanf("%d",&x) && x!=-1)
a[x]=0;
for(int i=1;i<9;++i)//枚举方案
{
int f=1;
for(int j=1;j<=n;++j)
{
if(a[j]==-1) continue; //没规定是亮还是暗
int temp = j%6;
if(temp==0) temp=6;
if(a[j]!=state[i][temp])
// if(a[j]!=state[i][j])
{
f=0;break;
}
}
if(f && (c >= minn[i] || (c==1 && i==4)) )//这种方案符合
{
char now[110];
num++;
for(int j=1;j<=n;++j)
{
int temp = j%6;
if(temp==0) temp=6;
now[j-1]=state[i][temp]+'0';
//now[j-1]=state[i][j]+'0';
}
now[n]='\0';
ans[num]=now;
}
}
sort(ans,ans+num);
for(int i=1;i<=num;++i)
puts(ans[i].c_str());
return 0;
}
</strong>
二、优化的dfs
1、通过几次操作会发现,按两次相同的按钮会抵消效果比如
按 112 等于 按 2
按 121 等于 按 2
所以最多只有4个不重复的按键,所以说不管按几次都能转化成按4个键的情况,比如按五次那么肯定有某个键按了两次效果会抵消相当于只按了三次,所以说,每次都相当于按4-c%2次,比如6,其实最多按4个次就能达到想要的状态。
2、然后就是简单的DFS,这里需要注意int型和char型的转化,还有就是结果可能会有相同的,比如 c = 6 并且是按123412得到的(实际上相当于只按了34),那么我们会先让c变成4,那么能达到34的可能是2234 可能是1134 等等,所以dfs时候肯定会搜出重复的,要去重。
贴一下代码
#include <cstring>
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#define INF 0x3f3f3f3f
const int maxn=5010;
using namespace std;
vector <string> ans;
int a[110];//目标串
int b[110];//符合条件的某个
char tem[110];
int num=0;//符合目标的个数
int n,c;
void num1()
{
for(int i=1;i<=n;++i)
{
b[i]=!b[i];
}
}
void num2()
{
for(int i=1;i<=n;i+=2)
{
b[i]=!b[i];
}
}
void num3()
{
for(int i=0;i<=n;i+=2)
{
b[i]=!b[i];
}
}
void num4()
{
for(int i=1;i<=n;i+=3)
{
b[i]=!b[i];
}
}
void dfs(int icount)
{
if(icount == c)
{
int f=1;
for(int i=1;i<=n;++i)
{
if(a[i]==-1) continue ;
if(a[i]!=b[i])
{
f=0;break;
}
}
if(f)
{
num++;
for(int i=0;i<n;++i)
tem[i]=b[i+1]+'0';
tem[n] = '\0';
ans.push_back(tem);
}
return ;
}
num1();
dfs(icount+1);
num1();
num2();
dfs(icount+1);
num2();
num3();
dfs(icount+1);
num3();
num4();
dfs(icount+1);
num4();
}
int main()
{
int x;
scanf("%d%d",&n,&c);
if(c>4) c=4-c%2;
//初始化
memset(a,-1,sizeof(a));
while(scanf("%d",&x) && x!=-1)
a[x]=1;
while(scanf("%d",&x) && x!=-1)
a[x]=0;
for(int i=0;i<=n;++i)
{
b[i]=1;
}
dfs(0);
sort(ans.begin(),ans.end());//排序
puts(ans[0].c_str());
for(int i=1;i<num;++i)
{
if(ans[i]!=ans[i-1]) //去重
puts(ans[i].c_str());
}
return 0;
}