1. 程式人生 > >BZOJ1080 劣質編碼

BZOJ1080 劣質編碼

非正解做法 , 時間不理想 , 輕噴……

建議先考慮二維的情形 , 訓練指南上字串那一章有類似題。
提示:
1. 怎麼樣簡潔的表達一個狀態呢? 其實用兩個字串就可以啦。
2. 怎麼樣轉移一個狀態 , 怎麼樣去掉避免重複 , 這是這個問題最讓人頭疼的地方。

詳細解釋在程式碼後:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <deque> #include <stack> #include <algorithm> #include <set> #include <map> #include <queue> #include <cassert> using namespace std; const int maxn = 35; const int INF = 0x3f3f3f3f; int n; char ss[maxn][60]; string s[maxn]; __inline string getBack(string
& s1 , string& s3) { return s1.substr(s3.size() , s1.size()-s3.size()); } struct state { string s1 , s2; state(){s1.clear(); s2.clear();} bool operator <(const state& b)const { return s1<b.s1 || (s1==b.s1 && s2<b.s2); } bool judge(string& s3) { if
(s3.size()<=s1.size()) return s3==s1.substr(0 , s3.size()); if(s3.size()<=s2.size()) return s3==s2.substr(0 , s3.size()); return s2 == s3.substr(0 , s2.size()); } state operator +(string& s3) { state res; if(s3.size()<=s1.size()) { res.s1 = getBack(s1, s3); res.s2 = getBack(s2, s3); return res; } if(s3.size()<=s2.size()) { res.s1 = getBack(s3, s1); res.s2 = getBack(s2, s1); return res; } res.s1 = getBack(s2, s1); res.s2 = getBack(s3, s1); return res; } }; map<state, int> dic; queue<state> q; int res = INF; void update() { while(!q.empty()) { state now = q.front() , ne; q.pop(); int step = dic[now]; for(int i=1;i<=n;i++) { if(!now.judge(s[i])) continue; ne = now+s[i]; if(!dic.count(ne) || dic[ne]>step+s[i].size()) { dic[ne] = step+s[i].size(); q.push(ne); } } } if(dic.count(state())) res = min(res , dic[state()]); } bool getState(string s1 , string s2 , string s3 , int d) { state now; if(s1.size()>s2.size()) swap(s1, s2); if(s1.size()>s3.size()) swap(s1, s3); if(s2.size()>s3.size()) swap(s2, s3); if(s1 != s2.substr(0 , s1.size())) return false; if(s2 != s3.substr(0 , s2.size())) return false; now.s1 = getBack(s2, s1); now.s2 = getBack(s3, s1); if(dic.count(now) && dic[now]<=d) return false; if(d+now.s2.size()*2-now.s1.size() >= res) return false; dic[now] = d; q.push(now); return true; } map<string , int> all; bool conbine(string s1 , string s2 , string& s3) { if(s1.size()>s2.size()) swap(s1, s2); if(s2.substr(0 , s1.size())!=s1) return false; s3 = getBack(s2, s1); return true; } void solve(map<string , int>::iterator i) { int step = i->second; for(int j=1;j<=n;j++) for(int k=j+1;k<=n;k++) if(getState(s[j], s[k], i->first, (step+(step-i->first.size())/2)+s[j].size()+s[k].size())) update(); for(int j=1;j<=n;j++) for(int k=j+1;k<=n;k++) if(getState(i->first+s[j], i->first+s[k], "", (step+(step+i->first.size())/2)+s[j].size()+s[k].size())) update(); } int main(int argc, char *argv[]) { scanf("%d" , &n); for(int i=0;i<=n;i++) gets(ss[i]); for(int i=1;i<=n;i++) for(int j=0;j<strlen(ss[i]);j++) if(isdigit(ss[i][j])) s[i]+= ss[i][j]; for(int i=1;i<=n;i++) if(s[i].size()==0) { puts("0"); return 0; } for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) for(int k=j+1;k<=n;k++) if(getState(s[i], s[j], s[k], s[i].size()+s[j].size()+s[k].size())) update(); queue<string> q; string nex; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(conbine(s[i], s[j], nex)) { q.push(nex); if(all.count(nex)==0 || all[nex]>s[i].size()+s[j].size()) { all[nex] = s[i].size()+s[j].size(); solve(all.find(nex)); } } while(!q.empty()) { string now = q.front(); q.pop(); int step = all[now]; if(step>=res) continue; for(int i=1;i<=n;i++) if(conbine(now, s[i], nex)) if(all.count(nex)==0 || all[nex]>step+s[i].size()) { all[nex] = step+s[i].size(); q.push(nex); solve(all.find(nex)); } } if(res==INF) puts("-1"); else printf("%d\n" , res/3); return 0; }

空串直接特判。

解釋:
1. 三種解碼方式不能完全相同就有兩種情形 , 要麼一開始就不相同 , 否則就是一開始有兩個相同 , 在某一個時刻變得不同。 絕對不可能三個一開始都相同。
2. 第一類情況好處理 , 一開始列舉是哪三個就可以。
3. 第二類情況我們需要先列舉二維的情況 , 也就是列舉兩個一開始構造相同的解碼方式的分界點 , 然後加上兩個不同的串 , 這樣狀態就合法了。

時間剛好卡住 , 有沒有好的優化方法呢? help