USACO Training Section 2.2 Party Lamps

本文解析了USACO竞赛中的Lamps问题,通过枚举按钮状态的方法找到所有可能的灯的状态组合。注意到按钮计数器的有效状态不超过4次,通过检查按键次数和奇偶性来验证按钮动作是否符合计数器的要求。
[url="http://ace.delos.com/usacoprob2?a=3nbwNTZTBNm&S=lamps"]英文原题[/url] [url="http://www.wzoi.org/usaco/13%5C104.asp"]中文题译[/url]
题目大意:
一组灯,几个按钮,按一个按钮可以改变若干灯的开关状态。给定按按钮的计数次数和最终状态中若干个灯的状态,要求求出所有可能状态。

每个灯只有两个状态:开/关。每个按钮如果按两次就会恢复原样。因此,按钮计数器C实际上只有最多4次有效。对C>4,枚举每个按钮的开关状态,一共2^4=16个状态,检查是否与目标状态吻合,即可得解。

需要注意之处:枚举按钮动作之后判断是否符合计数器要求用两个条件,一是按键次数二是奇偶性。

调试时发现一个错:memset( lamps, 1, sizeof(int)*n_lamp );试图把lamps初始化为1,但结果是0x01010101,因为memset是按byte操作的而lamps是整数值。另外,对静态二维数组排序,需要用指针排,或者,将内部一维数组声明成结构,不过不用指针排的话copy操作耗时(在这里倒是可以忽略不计)。



/*
ID: blackco3
TASK: lamps
LANG: C++
*/
#include <iostream>
#include <algorithm>
#include <memory.h>
using namespace std;
#define _max_ 100
int lamps[_max_], final[_max_], possible[16][_max_] ;
int n_lamp, n_act, n_possible=0 ;

bool p_psb_comp( int *lamps_1, int *lamps_2 ) {
for( int i=0; i<n_lamp; i++ )
if( lamps_1[i] < lamps_2[i] )
return true ;
else if ( lamps_1[i] > lamps_2[i] )
return false ;
return false ;
}
void check_lamps() {
for( int i=0 ; i<n_lamp ; i++ )
if( final[i]!=-1 && final[i]!=lamps[i] )
return ;
memcpy( possible[n_possible++], lamps, sizeof(lamps) );
}
inline void turn_lamps( int but ) {
for( int i=0; i<n_lamp ; i++ )
if( !but || ( but==1 && i&1 ) || ( but==2 && !(i&1) ) || ( but==3 && !(i%3) ) )
lamps[i] = 1-lamps[i] ;
}
void check_acts(int cur_act, int button)
{
if( button==4 ){
if( cur_act<=n_act && (cur_act&1)==(n_act&1) )
check_lamps();
return ;
}
check_acts(cur_act,button+1) ;
turn_lamps( button ) ;
check_acts(cur_act+1,button+1);
turn_lamps( button ) ;
}

int main() {
freopen("lamps.in", "r", stdin);
freopen("lamps.out", "w", stdout);
cin >> n_lamp >> n_act ;
memset( final, -1, sizeof(int)*n_lamp );
for( int cur=-1, icount=0 ; icount<2 ; ){
cin >> cur ;
if( cur==-1 )
icount++ ;
else
final[cur-1]=1-icount ;
}
for( int i=0; i<n_lamp; i++ )
lamps[i]=1 ;
check_acts(0,0);
if( !n_possible )
cout << "IMPOSSIBLE" << endl ;
else {
int *p_psb[16] ;
for( int i=0; i<n_possible; i++ )
p_psb[i]=possible[i] ;
sort( p_psb, p_psb+n_possible, p_psb_comp );
for( int i=0; i<n_possible; i++ ){
for( int j=0; j<n_lamp; j++ )
cout << p_psb[i][j] ;
cout << endl ;
}
}
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值