UVa1375- The Best Name for Your Baby
2015-10-20 17:17
316 查看
提示:
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;
}
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;
}
相关文章推荐
- 基于Android中dp和px之间进行转换的实现代码
- Android中dip、dp、sp、pt和px的区别详解
- LFC1.0.0 版本发布
- Android px、dp、sp之间相互转换
- android中像素单位dp、px、pt、sp的比较
- 1.10055 - Hashmat the brave warrior
- 2.10071 - Back to High School Physics
- 3.458 - The Decoder
- 4.694 - The Collatz Sequence
- 6.494 - Kindergarten Counting Game
- 7.490 - Rotating Sentences
- 8.414 - Machined Surfaces
- 9.488 - Triangle Wave
- A.457 - Linear Cellular Automata
- B.489 - Hangman Judge
- C.445 - Marvelous Mazes
- 1.10494 - If We Were a Child Again
- 2.424 - Integer Inquiry
- 3.10250 - The Other Two Trees
- 5.465 - Overflow