题目大意是对一个n层,每层有m个位置的书架进行4种操作:
1)在位置(i,j)上放一本书,如果已经有书,则不在方书
2)拿走位置(i,j)上的一本书,如果没书,则不能拿走
3)将第i层的所有位置的状态翻转,如果某位子有书,则拿走,如果某位置没书,则放一本书
4)将书架的状态还原到之前的某个状态
比赛的时候这个题目并没有思路,最后看了别人思路。自己写了一下。觉得方法比较巧妙,所以写博客记之,以便以后回顾。题目要求每次操作的后输出当前书架上的书的数量。我们需要将操作看成边,当前的状态看作节点,建树。建树的规则是,如果进行的操作树1或者2或者3,那么就将操作后状态连接在当前状态后面,如果操作是4,就将操作后的状态连接在要还原的状态后面。然后进行dfs,离线求解。dfs的巧妙之处在于,由于数据量比较大,不可能将每次操作后的状态都记下来。其实,仔细想想,根本不需要记录所有的状态,只需要记录当前状态,然后dfs,回溯的时候将更改的状态在改回来。这样,一边dfs就解决问题。时间复杂度为q*m。下面贴上代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int MAX = 100010;
const int MAXN = 1010;
struct Node{
int x,y,z;
}p[MAX];
vector<int>G[MAX];
int mark[MAXN][MAXN];
int d[MAX],n,m,q;
void init(){
d[0] = 0;
memset(mark, 0, sizeof(mark));
for (int i = 0; i<MAX; i++) G[i].clear();
}
void dfs(int u){
for (int i = 0; i<G[u].size(); i++){
int v = G[u][i];
int s = p[v].y, t = p[v].z;
if (p[v].x == 1){
if (mark[s][t] == 1){
d[v] = d[u];
dfs(v);
}
else{
mark[s][t] = 1;
d[v] = d[u] + 1;
dfs(v);
mark[s][t] = 0;
}
}
if (p[v].x == 2){
if (mark[s][t] == 0){
d[v] = d[u];
dfs(v);
}
else{
mark[s][t] = 0;
d[v] = d[u] - 1;
dfs(v);
mark[s][t] = 1;
}
}
if (p[v].x == 3){
int cnt = 0;
for (int j = 1; j<=m; j++){
if (mark[s][j] == 1){
cnt--;
mark[s][j] = 0;
}
else{
cnt++;
mark[s][j] = 1;
}
}
d[v] = d[u] + cnt;
dfs(v);
for (int j = 1; j<=m; j++){
if (mark[s][j] == 0){
mark[s][j] = 1;
}
else{
mark[s][j] = 0;
}
}
}
if (p[v].x == 4){
d[v] = d[u];
dfs(v);
}
}
}
int main(){
while (scanf("%d%d%d",&n,&m, &q) != EOF){
init();
for (int i = 1; i<=q; i++){
scanf("%d", &p[i].x);
if (p[i].x==1 || p[i].x == 2){
scanf("%d%d",&p[i].y, &p[i].z);
G[i-1].push_back(i);
}
if (p[i].x == 3){
scanf("%d", &p[i].y);
G[i-1].push_back(i);
}
if (p[i].x == 4){
scanf("%d", &p[i].y);
G[p[i].y].push_back(i);
}
}
dfs(0);
for (int i = 1; i<=q; i++) printf("%d\n", d[i]);
}
return 0;
}

本文介绍了一种解决书架状态模拟问题的算法。通过构建树结构来表示书架的不同状态及转换过程,并采用DFS实现离线求解。具体地,将操作视为边,状态视为节点,巧妙地利用回溯来更新书架状态。

被折叠的 条评论
为什么被折叠?



