题解 P3417 【[POI2005]BANK-Cash Dispenser】

小黑AWM

2019-02-11 21:35:28

Solution

##### 看到提交记录中很多人的代码和zyl的长得一模一样,~~屎名警告哦~~qwq *** 在z老师课上一开始茫然半天才弄明白题意,也没太看懂周老师的标程,不过还是十分感谢POI搬运组。 题意oscar巨巨已经简述过了,在此不再赘述,本题解主要提供一种不太一样的写法以及思考过程并且说一下坑点。做法直接跳转到第9段。 *** 不难想到我们要枚举0000 - 9999的所有密码输入序列依次检验,不过检验过程中如果强行代入t个动作序列,那么检验的复杂度便是$O(\sum t)$。这必然是不可取的。 那么不妨做一遍LCS再对LCS进行枚举?做一遍$n = 1e3,\space t= 1e4$的LCS的复杂度怕是要凉。 根据题目性质我们可以增加一些思考,我们手头有的只是四个数字的序列, 我们只要在所有的$t$个序列中找到_有这四个数字的子序列就行了_。 因此我们不需要这个数字的下一个是什么,我们只要知道我们密码数字的后一位在哪儿,寻找过去就是了。 考虑用一个NXT[i][j][k]的数组表示在第i行,第j列,之后(由于我们可以在一个数字上按很多次所以包括其本身)最近的数字k($k\in[0,9],\space k\in N$)的位置 这个东西只要倒过来维护就可以了。 ```cpp #include <cstdio> #include <iostream> #define Int int using namespace std; const int maxn = 1e3 + 10; const int maxt = 1e4 + 10; int n, t, nxt[maxn][maxt][10], cnt; char input[maxt]; bool check(int xx, int row){ int x[5]; x[1] = xx/1000; x[2] = (xx/100)%10; x[3] = (xx/10)%10; x[4] = xx%10; int at = 1; for(int i = 1; i <= 4; i++){ if(nxt[row][at][x[i]] == 0) return false; at = nxt[row][at][x[i]]; } return true; } bool check(int xx){ for(int i = 1; i <= n; i++) if(!check(xx, i)) return false; return true; } int main(){ cin >> n; for(int i = 1; i <= n; i++){ cin >> t; cin >> input; for(int j = t; j >= 1; j--){ for(int k = 0; k < 10; k++){ if(k == input[j-1] - '0') nxt[i][j][k] = j; else nxt[i][j][k] = nxt[i][j+1][k]; } } } for(int i = 0; i < 10000; i++) cnt += check(i); cout << cnt << endl; return 0; } //60分 ``` 嗯,提交,MLE。这如果是在NOIP是要爆零的! 我们计算一下空间复杂度,光nxt就有1e8*4bit。[束手无措.jpg]╮( ̄▽ ̄"")╭ 脑子钝了好久愣是没想出来怎么优化空间。考虑提取公因式,发现在检验和预处理中在第几行都出现过了,那么我们能不能在每一行就检验一下呢,这样nxt就可以省下n这一维。 ```cpp #include <cstdio> #include <iostream> #include <cstring> #define Int int using namespace std; const int maxn = 1e3 + 10; const int maxt = 1e4 + 10; int n, t, nxt[maxt][10], cnt; char input[maxt]; bool great[10000]; bool check(int xx){ int x[5]; x[1] = xx/1000, x[2] = (xx/100)%10, x[3] = (xx/10)%10, x[4] = xx%10; int at = 1; for(int i = 1; i <= 4; i++){ if(nxt[at][x[i]] == 0) return false; at = nxt[at][x[i]]; } return true; } int main(){ memset(great, 1, sizeof(great)); cin >> n; for(int i = 1; i <= n; i++){ memset(nxt, 0, sizeof(nxt)); cin >> t; cin >> input; for(int j = t; j >= 1; j--){ for(int k = 0; k < 10; k++){ if(k == input[j-1] - '0') nxt[j][k] = j; else nxt[j][k] = nxt[j+1][k]; } } for(int j = 0; j < 10000; j++) great[j] &= check(j); } for(int i = 0; i < 10000; i++) cnt += great[i]; cout << cnt << endl; return 0; } //AC ``` # 大功告成!