UVa 10618 Fun Game
题目:
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=36035
思路:
一圈人围坐,给出小球的传递序列,求解最少有多少个人。
问题简单化:如果有一排人,单向传递,给出序列求解最少多少人。那么问题就是:求解一个最短序列使得给出的所有序列都是该序列的连续子序列。
再分别解决以下问题:
- 第一个人可以向左向右传递: 每个传递序列有两种情况,分别是正序与逆序。因为不清楚当前序列是向左传还是向右传。
- 传递是一个环:规定将第一个序列正向串作为首序列,累计答案的时候只需要算上重叠部分即可。
- 输入可能是绕过好几圈的情况:如果绕过好几圈,那么其他的字串因已经被包含而舍弃,dp过程不会进行,最后累计ans时ans=len[0]-重叠(s[0][0],s[0][0])即最小循环节的长度。
- 输入保证n>=2:当ans<=1是修改为2
代码:
1 #include<iostream> 2 #include<cstring> 3 #include<vector> 4 #include<algorithm> 5 #define FOR(a,b,c) for(int a=(b);a<(c);a++) 6 using namespace std; 7 8 const int maxn = 16; 9 const int maxlen= 100 + 5; 10 11 struct Node{ 12 string s,rev; 13 bool operator <(const Node& rhs) const{ 14 return s.size()<rhs.s.size(); //size由小到大 15 } 16 }; 17 18 int n; 19 int d[1<<maxn][maxn][2]; 20 int overlap[maxn][maxn][2][2]; 21 string s[maxn][2]; 22 int len[maxn]; 23 24 //return a右 && b左的重叠部分 25 int make_overlap(const string& a,const string& b){ 26 int n1=a.size(); 27 int n2=b.size(); 28 FOR(i,1,n1) { //找到最小重合点 即返回最大重合长度 29 if(n2 + i <= n1) continue; 30 bool ok=true; 31 for(int j=0;i+j<n1;j++) 32 if(a[i+j] != b[j]) { ok=false; break; } 33 if(ok) return n1-i; 34 } 35 return 0; //无重合 36 } 37 38 void init() { 39 Node nodes[maxn]; 40 FOR(i,0,n) { 41 cin>>nodes[i].s; 42 nodes[i].rev=nodes[i].s; 43 reverse(nodes[i].rev.begin(),nodes[i].rev.end()); //反转串 44 } 45 46 sort(nodes,nodes+n); //sort 47 int nn=0; 48 FOR(i,0,n) { 49 bool need=true; 50 FOR(j,i+1,n) 51 if(nodes[j].s.find(nodes[i].s) != string::npos || 52 nodes[j].rev.find(nodes[i].s) != string::npos){ //如果被包含则舍弃 53 need=false; break; 54 } 55 if(need) { 56 s[nn][0]=nodes[i].s; 57 s[nn][1]=nodes[i].rev; 58 len[nn]=s[nn][0].size(); 59 nn++; 60 } 61 } 62 n=nn; 63 64 //构造重叠数组 65 FOR(i,0,n) FOR(x,0,2) 66 FOR(j,0,n) FOR(y,0,2) 67 overlap[i][j][x][y]=make_overlap(s[i][x],s[j][y]); 68 } 69 70 inline void update(int& x,int v) { 71 if(x<0 || v<x) x=v; 72 } 73 74 void solve() { 75 memset(d,-1,sizeof(d)); 76 d[1][0][0]=len[0]; //始终把s[0][0]放在首位 77 78 int full=1<<n; 79 FOR(s,1,full) 80 FOR(i,0,n) FOR(x,0,2) if(d[s][i][x]>=0) //已求过集合中的枚举 81 FOR(j,1,n) if(!((1<<j) & s)) FOR(y,0,2) //刷表更新的目标枚举 82 update(d[s|(1<<j)][j][y], d[s][i][x] + len[j]-overlap[i][j][x][y]); 83 84 int ans=-1; full--; 85 FOR(i,0,n) FOR(x,0,2) { 86 if(d[full][i][x]<0) continue; 87 update(ans, d[full][i][x] - overlap[i][0][x][0]); //ans累计 枚举尾字串并减去与第一个串的重叠部分 88 } 89 if(ans<=1) cout<<"2 "; //题目中明确给出 n>=2 90 else 91 cout<<ans<<" "; 92 } 93 int main(){ 94 while(cin>>n && n) { 95 init(); 96 solve(); 97 } 98 return 0; 99 }