usaco2.2 lamps

来源:互联网 发布:mac中截屏键 编辑:程序博客网 时间:2024/05/16 06:10

题目链接


(最近以来好不容易让我感到自己智商上线的一道题(叹


题意:

最初的状态是 1-N 灯全开着

接下来进行一系列(4种)操作(操作次数 C 多达 1w)

1:将所有灯的状态反转

2:将偶数号的状态反转

3:将奇数号的状态反转

4:将 3k+1 号的状态反转

得到最终状态

现给出最终状态中的 一些 灯的状态

求所有可能的最终状态,并升序排列


思路:

无脑递归显然不可行,10000 层递归显然是开玩笑

那么肯定有什么聪明的办法


显然,操作的最终状态与操作顺序无关

设四种步骤的操作数分别为 a, b, c, d

则我们有 

a * step1 + b * step2 + c* step3 + d * step4 == FinalState - InitialState;

a + b + c + d == n;

(stepi 都是 01 位串

(第一个等式意思意思写得不严谨,大概含义就是 每一位上 进行的 所有的操作 累加起来的 1 的总和(模上一个2) 导致的这一位发生的改变,即为最终状态与起始状态之间的改变((当然只需要检查那些给定状态的位

写到这里我们很容易就能发现,对于每个 stepi,一旦进行两次,%2 之后改变量就全是 0,就相当于什么都没做

所以我们可以将第一个式子变为

a%2 * step1 + b%2 * step2 + c%2 * step3 + d%2 * step4 == FinalState - InitialState;

也就是说,对于每一个 stepi 都只有进行和不进行的两种情况,总共也就只有 2^4 = 16 种情况

另外因为我们是两个两个抵消的,还要满足的一个要求就是 (n - ∑ step) % 2 == 0; 或者也可以 ∑ step <= n;


说到这里,怎么做就很显然了


AC代码如下:

(末尾有和 ANALYSIS 的对比反思)

/*PROB: lampsLANG: C++ID: fan_0111*/#include <iostream>#include <cstdio>#include <string>#include <algorithm>using namespace std;int pos[110], line[110], sum[110], change[4][110], step[4], ans[110];string s[100010];int tot = 0, c, n;void in(int state) {int x;while (cin >> x && x != -1) {pos[tot++] = x;line[x] = state;}}bool check1() {for (int i = 0; i < tot; ++i) {if (line[pos[i]] != (sum[pos[i]] & 1)) return false;}return true;}bool check2() {int Step = step[0] + step[1] + step[2] + step[3];return !(((c - step[0] - step[1] - step[2] - step[3]) & 1) || Step > c);}void modify(int x, int sign) {for (int i = 1; i <= n; ++i) {sum[i] += change[x][i] * sign;}}void dfs(int x, int choice, int& totAns) {step[x] = choice;if (x == 3) {if (check1() && check2()) {for (int i = 1; i <= n; ++i) ans[i] = 1;for (int i = 0; i < 4; ++i) {if (step[i] == 0) continue;for (int j = 1; j <= n; ++j) {ans[j] = (ans[j] + change[i][j]) % 2;}}for (int i = 1; i <= n; ++i) {s[totAns] += ans[i] + '0';}++totAns;}return;}dfs(x+1, 0, totAns);modify(x+1, 1);dfs(x+1, 1, totAns);modify(x+1, -1);}int main() {freopen("lamps.in", "r", stdin);freopen("lamps.out", "w", stdout);cin >> n >> c;in(0);in(1);sort(pos, pos+tot);for (int i = 1; i <= n; ++i)change[0][i] = 1;for (int i = 1; i <= n; i += 2) change[1][i] = 1;for (int i = 2; i <= n; i += 2) change[2][i] = 1;for (int i = 1; i <= n; i += 3) change[3][i] = 1;int totAns = 0;dfs(0, 0, totAns);modify(0, 1);dfs(0, 1, totAns);sort(s, s+totAns);if (totAns == 0) cout << "IMPOSSIBLE\n";else {for (int i = 0; i < totAns; ++i) {cout << s[i] << endl;}}fclose(stdin);fclose(stdout);return 0;}


后来看了 ANALYSIS, 发现除了这个以外,还可以有一个优化

那就是事实上我们这里的所有操作,产生的效果其实是 每 6 位一循环的

所以我们可以只看前 6 位的状态,而不是去看 N 位

这里的 N (10-100) 还比较小,如果大一点的话,可能就会被卡掉了

所以只看 6 位是一个很有意义的优化


并且 ANALYSIS 里的代码用了很多位运算

比我用数组去存很多东西,最后对 01 位串的字符串去排序的做法高明了很多

很值得学习


还有就是 ANALYSIS 里的递归,也和我用的不一样

我是对每个 step 去递归取货不取,就像是 打印一个集合的全部子集 的做法一样

先对不取 dfs,加进了取的状况,再 dfs

而 ANALYSIS 里先是规定一些给定的步数情况(这是可以做到的),对还剩余的步骤数可以取什么进行 dfs,也是一个很常规的思路


总得来说觉得 ANALYSIS 里的代码写得还是很美的

附上 ANALYSIS 代码:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h>#define MAXLAMP6#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. */voidsearch(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);}voidprintseq(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);}voidmain(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);}


0 0
原创粉丝点击