POJ 2161 Chandelier(树状DP)
一、题意
首先是对题目的翻译。给出一个长长的字符串,这个字符串描述了一个吊灯。对于给字符串只有两种操作数——'a'为一个吊灯灯珠,将改灯珠入栈,一位阿拉伯数字K,代表一个环,将把该数字前面k位数都出栈并且穿成一个环,并将该环重新入栈(作为一个单元)。由此可以得到一颗神奇的树——每个节点的若干子节点呈现循环数组的关系。因而此处有对于同构的定义为:再该环上各个小串的相对位置不变。于是,要求一个新的字符串,能够成上述字符转的一个同构的树,在这个基础上求出最小的“最大栈空间”大小。
二、思路
首先设dp[i]为将第i个节点及其子树全部入栈的最小栈大小。对于其第一个入栈的子树,认为此时的最高栈高度为k,则dp[i]=main(dp[i],dp[tar]+i)。此时应当枚举起点并且找到使得值最小的起点。
(红书说是使用单调栈实现O(N)的求出这个值得具体大小,但是经过隔壁队YC大佬指点发现不是单调栈而是一个简单思路(复杂度O(n))对于每个起点的特定顺序,必然有,最大值为上一个的最大值-1,或者上一个的起点+m-1。于是这样就可以O(1)的找出每一组的最大值,直接对比就好)
//#include<bits/stdc++.h> #include<vector> #include<stack> #include<iostream> #include<string.h> #include<stdio.h> using namespace std; #define veci vector<int> #define stai stack<int> const long long MAXN=1e4+233; veci G[MAXN]; stai ss; char str[MAXN]; int length; int dp[MAXN]; int pos[MAXN]; int deal(int now) { int len=G[now].size(); int maxx=dp[G[now][0]]; for(int i=0;i<len;++i) { maxx=max(maxx,dp[G[now][i]]+i); } int ans=maxx; for(int i=1;i<len;++i) { maxx=max(maxx-1,dp[G[now][i-1]]+len-1); if(ans>maxx) { pos[now]=i; ans=maxx; } } return ans; } void dfs(int now) { int len=G[now].size(); if(len==0) { dp[now]=1;return ; } for(int i=0;i<len;++i) { int tar=G[now][i]; dfs(tar); } dp[now]=deal(now); } void show(int now) { int len=G[now].size(); if(len==0) { cout<<"a";return ; } for(int i=0;i<len;++i) { int pp=pos[now]+i; pp%=len; int tar=G[now][pp]; show(tar); } cout<<G[now].size(); } void init() { // gets(str+1); length=strlen(str+1); memset(dp,0,sizeof(dp)); memset(pos,0,sizeof(pos)); int ll=0; for(int i=1;i<=length;++i) { G[i].clear(); ll=max(ll,(int)ss.size()); if(str[i]!='a') { int len=str[i]-'0'; stai s2; for(int j=0;j<len;++j) { // int tar=ss.top(); s2.push(ss.top()); ss.pop(); // G[i].push_front(tar); // G[i].push_back(tar); } while(!s2.empty()) { G[i].push_back(s2.top()); s2.pop(); } } ss.push(i); } dfs(length); cout<<dp[length]<<"\n"; show(length); cout<<"\n"; // cout<<endl<<ll<<endl; } int main() { cin.sync_with_stdio(false); while(cin>>(str+1))init(); return 0; }