UVA - 1375 The Best Name for Your Baby (有’环‘的动态规划)

问题

分析

参考:
https://blog.csdn.net/hyqsblog/article/details/46909863
https://github.com/aoapc-book/aoapc-bac2nd/blob/master/ch9/uva1375.cpp
https://blog.csdn.net/Fuxey/article/details/49279917
https://blog.csdn.net/u014258433/article/details/69070747

这道题感觉是这章到目前最抽象的一题,无论是思路还是解法都很难懂,我看了代码仓库中的代码,勉强看懂,但是下次估计还是做不出来,简单记载一下自己的理解

目标:求符合要求最小字典序的字符串

  1. 把上下文无关文法分解成基本的的结构A->BC这种形式,然后拆分后规则总数不超过maxn*(maxlen-1)个
  2. 定义状态d(i,L)是字符串i能够变成长度L的最小字典序字符串,i是字符串在字典中的序号
  3. 然后对于这些状态,将他们看作是一张图中的节点,状态之间的转移关系是由拆分后的规则决定的,这一步说起来简单,但是实际代码比较复杂
  4. 状态转移方程:分为两种转移方式,
    不同层之间的:L进行拆分, d p ( i , L ) = m i n ( d p ( j , p ) + d p ( k , L p ) ) , i > j k 0 < p < L dp(i,L)=min(dp(j,p)+dp(k,L-p)),其中i->jk,0<p<L
    同层间的:L不变 d p ( i , L ) = m i n ( d p ( k , L ) ) , i > j k i > k j , d p [ j ] [ 0 ] = = " " dp(i,L)=min(dp(k,L)), i->jk或者i->kj,dp[j][0]==""
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
using namespace std;
const int maxn=50+5;  //rule的个数
const int maxlen=20+5; //name长度
const int maxs=50*20+100; //对于每一条规则,长度x,按照S=PA拆分,P是一个字母,A是字符串,能够产生x-1个规则
struct Transform{
    int target,empty;
    Transform(int t,int e):target(t),empty(e){}
};
int n,L,ns;  //ns denotes number of sym
string rules[maxn];
string sym[maxs];   //id2sym
map<string,int> sym2id;  //sym2id
string dp[maxs][maxlen];  //状态
vector<Transform> tr[maxs];  //记录转换过程   tr[i]表示序号为i的字符串可以 i+empty->target 或者 empty+i->target
int head[maxs],tail[maxs];  //存储S=PA拆分后的字符串

struct Node{
    int x;
    string s;
    Node(int x,string s):x(x),s(s) {}
    bool operator < (const Node &rhs) const {
        return s>rhs.s;  //为了在优先队列中从小到大排列
    }
};

int ID(const string &s){
    if(sym2id.count(s)) return sym2id[s];
    sym[ns]=s;
    return sym2id[s]=ns++;
}

//将规则进行拆分
void add_inter_symbol(const string &str){
    int id=ID(str);
    int len=str.length();
    if(len<2) return;
    int h=ID(str.substr(0,1));
    int t=ID(str.substr(1));
    tr[h].push_back(Transform(id,t));
    tr[t].push_back(Transform(id,h));
    head[id]=h; tail[id]=t;
}

//判断是否全部都是小数
bool isLowercase(const string &s){
    for(int i=0;i<s.length();++i){
        if(!(s[i]>='a' && s[i]<='z')) return false;
    }
    return true;
}

string min(const string &s1,const string &s2){
    if(s1=="-") return s2;
    return (s1<s2)?s1:s2;
}

bool vis[maxs];
//如果存在这样的规则 S->ij or S->ji,且dp[i][0]存在,那么就可以更新dp[id(S)][len]
void search(int len){
    memset(vis,0,sizeof(vis));
    priority_queue<Node>q;
    for(int i=0;i<ns;i++) {
        if(dp[i][len]!="-"){
            q.push(Node(i,dp[i][len]));
        }
    }
    while(!q.empty()) {
        Node u=q.top();
        q.pop();
        int x=u.x;
        string s=u.s;
        if(vis[x]) continue;
        vis[x]=true;
        for(int i=0;i<tr[x].size();++i){
            int target=tr[x][i].target;
            int empty=tr[x][i].empty;
            if(dp[empty][0]=="" && (dp[target][len]=="-" || s<dp[target][len])){
                dp[target][len]=s;
                q.push(Node(target,s));
            }
        }
    }
}

int main(void){
    while(cin>>n>>L && n){
        sym2id.clear();
        ns=0;
        ID("");
        for(int i=0;i<maxs;++i) tr[i].clear();
        for(int i=0;i<n;++i){
            cin>>rules[i];
            int left=ID(rules[i].substr(0,1));
            int right=ID(rules[i].substr(2));
//            tr[left].push_back(Transform(right,ID("")));
            tr[right].push_back(Transform(left,ID("")));  //S=AbC  AbC can be transformed to S
            int len=rules[i].length();
            for(int k=2;k<len;++k){
                add_inter_symbol(rules[i].substr(k));
            }
        }
        //初始化,"-"代表不可达
        for(int i=0;i<ns;++i){
            for(int j=0;j<=L;++j){
                dp[i][j]="-";
            }
        }
        dp[0][0]="";  // dp[i][j] means the first string with len j that symbol i can be transformed into
        for(int j=0;j<=L;++j){
            for(int i=0;i<ns;++i){
                if(sym[i].length()==j && (isLowercase(sym[i]))) dp[i][j]=sym[i];
                if(sym[i].length()<2) continue;
                //下面的步骤至少要有2个字母才能进行,不同层之间的状态转换
                int s1=head[i],s2=tail[i];
                for(int k=1;k<j;++k){
                    if(dp[s1][k]!="-" && dp[s2][j-k]!="-")
                        dp[i][j]=min(dp[i][j],dp[s1][k]+dp[s2][j-k]);
                }
            }
            search(j);  //同层之间的状态转换
        }
        cout<< dp[ID("S")][L] << "\n";
    }
}
发布了15 篇原创文章 · 获赞 0 · 访问量 163

猜你喜欢

转载自blog.csdn.net/zpf1998/article/details/104055715