山东济南彤昌机械科技有限公司 山东济南江鹏工贸游有限公司

【暑假】[深入动态规划]UVa 10618 Fun Game

UVa 10618 Fun Game

 

题目:

  http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=36035

思路:

  一圈人围坐,给出小球的传递序列,求解最少有多少个人。

  问题简单化:如果有一排人,单向传递,给出序列求解最少多少人。那么问题就是:求解一个最短序列使得给出的所有序列都是该序列的连续子序列。

  再分别解决以下问题:

  1.     第一个人可以向左向右传递: 每个传递序列有两种情况,分别是正序与逆序。因为不清楚当前序列是向左传还是向右传。
  2.     传递是一个环:规定将第一个序列正向串作为首序列,累计答案的时候只需要算上重叠部分即可。
  3.     输入可能是绕过好几圈的情况:如果绕过好几圈,那么其他的字串因已经被包含而舍弃,dp过程不会进行,最后累计ans时ans=len[0]-重叠(s[0][0],s[0][0])即最小循环节的长度。
  4.     输入保证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";       //题目中明确给出 n>=2 
90     else 
91      cout<<ans<<"\n";
92 }
93 int main(){
94     while(cin>>n && n) {
95         init();
96         solve();
97     }
98     return 0;
99 }

 

posted on 2015-08-19 12:43  hahalidaxin  阅读(349)  评论(0编辑  收藏  举报