提示:
1. 这是一个dp题 , 不要妄想用bfs来写此题 , 样例都过不了
2. dp方程的含义: d [ i ] [ j ] , 用一个大写字母(i+'A') 扩展成长度为j的字典序最小的字符串(不含大写字母)
此处请思考些时间 , 怎么转移状态;
3. 下面是我自己的转移方法 , 与紫书上的不同 , 紫书上把边拆分成了很多块 , 但对于初学做这个题的人来说 , 把边颠过来倒过去就够呛了 , 所以我的解法在保证时间的情况下尽量去降低代码和理解的难度:
下文中所有的状态指d[i][j] , 所有的边指的是原题中形如"A=Sbs.."的文法
首先考虑转移方程:(分成同层和非同层两个部分)
对于d [ i ] [ j ] ,遍历 i 的每一条边 , 尝试通过这些边来从非同层的状态转移(有一点抽象 , 想象把一个字符串中的所有大写字母换成另一字符串 , 并拼接在一起) , 这就是我们要做的事情 ,类似于每一个大写字母贡献一个长度 , 然后小写字母呆在原来的位置, 至于非同层状态是指大写字母贡献的那个长度要严格小于 j (即是现在计算的长度)
同层dp , 就是一个dijktra 算法 , 因为在同一层中d值我们只用较小的状态去更新较大的状态 , 与dijkstra的思想不谋而合
如果能够同层dp , i 肯定有这样一些转换边 , 转换出来的字符串都是大写字母 , 我们从中选择一个大写字母来p , 用d[p][j]来更新 d[i][j] , 当然除了p以外的所有大写字母都可以直接或间接的变成空串.
答案就是 d[ 'S'-'A' ][ l ]
注意: 这个dp的起始需要特殊判断 , 即要知道哪些大写字母可以转化成空串 , 我用了一个bellmanFord算法 , 不知道是否有更简洁的做法呢
// // main.cpp // UVa1375_NEW+ // // Created by Fuxey on 15/10/20. // Copyright © 2015年 corn.crimsonresearch. All rights reserved. // #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <list> #include <stack> #include <vector> #include <deque> #include <set> #include <map> #include <string> #include <algorithm> using namespace std; // d[i][j] using uppercase i+'A' to make a the minist string of length j struct edge { int u , all; string to; edge(int u = 0,string to = ""):u(u),to(to){} }; vector<edge> e; bool zero[30]; inline int id(char c){ return c-'A'; } vector<int> g[30]; int can[30][30]; string d[30][30]; int main(int argc, const char * argv[]) { int n , l; while(cin>>n>>l && n+l) { memset(zero, 0, sizeof(zero)); e.clear(); for(int i=0;i<26;i++) g[i].clear(); memset(can, 0, sizeof(can)); while(n--) { string op; cin>>op; if(op.size()==2) zero[id(op[0])] = 1; else g[id(op[0])].push_back((int)e.size()); e.push_back(edge(id(op[0]) , op.substr(2,op.size()-2))); e.back().all = 1; for(int i=2;i<op.size();i++) if(islower(op[i])) { e.back().all = 0; break; } } bool update = true; for(int i=1;i<=26 && update; i++) { update = false; for(int j=0;j<e.size();j++) if(e[j].all && !zero[e[j].u]) { bool ok = true; for(int k=0;k<e[j].to.size();k++) if(!zero[id(e[j].to[k])]) { ok = false; break; } if(ok) zero[e[j].u] = 1 , update = true; } } for(int i=0;i<e.size();i++) if(e[i].all) { bool ok = true; for(int j=0;j<e[i].to.size();j++) if(!zero[id(e[i].to[j])]) { ok = false; break; } if(ok) for(int j=0;j<e[i].to.size();j++) can[id(e[i].to[j])][e[i].u] = 1; else { int cnt = 0 , wh = 0; for(int j=0;j<e[i].to.size();j++) if(!zero[id(e[i].to[j])]) cnt++ , wh = id(e[i].to[j]); if(cnt==1) can[wh][e[i].u] = 1; } } for(int i=0;i<26;i++) for(int j=0;j<=l;j++) d[i][j] = "{"; for(int i=0;i<26;i++) if(zero[i]) d[i][0] = ""; string now[30]; int book[30]; for(int i=1;i<=l;i++) { for(int j=0;j<26;j++) for(int k=0;k<g[j].size();k++) // I am calculating d[j][i] { edge& enow = e[g[j][k]]; int cnt = 0; for(int ll = 0;ll<=i;ll++) now[ll] = "{"; now[0]=""; for(int ll = 0;ll<enow.to.size();ll++) { if(islower(enow.to[ll])) { cnt++; for(int q=i;q>=cnt;q--) if(now[q-1]!="{") now[q] = now[q-1]+enow.to[ll]; else now[q] = "{"; now[cnt-1] = "{"; // it is impossible } else { for(int q = i;q>=cnt;q--) { string Min = "{"; // each letter must do something to the answer , or it can not be successful for(int p = min(q-cnt,i-1);p>=0;p--)if(d[id(enow.to[ll])][p]!="{") Min = min(Min , now[q-p]+d[id(enow.to[ll])][p]); now[q] = Min; } } } d[j][i] = min(d[j][i] , now[i]); } // samelevel kind memset(book, 0, sizeof(book)); for(int j=1;j<=26;j++) { string Min = "{" ; int x = 0; for(int k=0;k<26;k++) if(!book[k] && d[k][i]<=Min) Min = d[k][i] , x = k; book[x] = 1; for(int k=0;k<26;k++) if(can[x][k]) d[k][i] = min(d[k][i] , Min); } } if(d[id('S')][l]=="{") cout<<"-\n"; else cout<<d[id('S')][l]<<endl; } return 0; }