poj2052 Fun Game

175 篇文章 0 订阅
125 篇文章 0 订阅

Description A few kids are standing around an old tree playing a game.
The tree is so huge that each kid can only see the kids close to
him/her.

The game consists many ‘turns’. At the beginning of each turn of the
game, a piece of paper is given to a randomly chosen kid. This kid
writes the letter “B” if he is a boy or the letter “G” if a girl. Then
he chooses a direction to pass the paper (clockwise or
counter-clockwise), and gives the paper to his neighbor in that
direction. The kid getting the paper writes down his sex too, and
gives the paper to his neighbor in the same direction. In this way,
the paper goes through the kids one by one, until one kid stops
passing the paper and announces the end of this turn.

For example, there are five kids around the tree, and their genders
are shown in Figure-1. The paper first goes to Kid1, after writing a
“B” he passes it to Kid2, and Kid2 to Kid3. After Kid3 writes down a
“G”, she ends up this turn, and we get the paper with a string “BBG”.

After N turns, we get N pieces of paper with strings of “B”s and/or
“G”s. One of the kids will get all these papers, and has to figure out
at least how many kids are around the tree playing the game. It’s
known that there are at least two kids. Please write a program to help
him.

这里写图片描述
Input There are several test cases. Each case starts with a line
containing an integer N, the number of papers (2 <= N <= 16). Each of
the following N lines contains a string on a paper, which is a
nonempty string of letter “B”s and/or “G”s. Each string has no more
than 100 letters.

A test case of N = 0 indicates the end of input, and should not be
processed.

Output For each test case, output the least possible number of kids in

如果一个串完全包含于另外一个的话,这个串就是没有用的,可以把它删去。接下来的操作都是对于任意一个串不是另一个的子串进行。
最简单的方法就是把所有串按顺序连接,这样结果是串长的总和。为什么答案会变优呢?因为有一些重叠的部分。容易发现,每相邻两个串【注意,只计算相邻的两个】重叠多长,答案就减少多少。
预处理出任意两个串一个接在另一个后面的最大重叠长度,然后用dp[i][j]表示选了集合i中的元素,最后一个是j的最大重叠长度,然后枚举下一个接在后面的字符串。
注意字符串正放和反放的情况【也就是原题中顺时针和逆时针】。
考虑到环,可以强行规定第一个位置放正放的第一个串,然后对答案加上末尾串接正放第一串的重叠长度。

#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
using namespace std;
struct seq
{
    char s[2][110];
    int l;
}a[20],f[20];
int n,n1,ovl[20][2][20][2],dp[1<<17][20][2];
bool out[20];
char temp[110];
void in()
{
    int i,j;
    for (i=1;i<=n;i++)
    {
        scanf("%s",a[i].s[0]+1);
        a[i].l=strlen(a[i].s[0]+1);
        strcpy(temp+1,a[i].s[0]+1);
        for (j=1;j<=a[i].l;j++)
          a[i].s[1][j]=temp[a[i].l-j+1];
    }
}
bool inc(char* s1,int l1,char* s2,int l2)
{
    if (l1>l2) return 0;
    int i,j;
    bool flag;
    for (i=1;i+l1-1<=l2;i++)
    {
        flag=1;
        for (j=1;j<=l1;j++)
          if (s1[j]!=s2[i+j-1])
          {
            flag=0;
            break;
          }
        if (flag) return 1;
    }
    return 0;
}
int cal (char* s1,int l1,char* s2,int l2)
{
    int i,j;
    bool flag;
    for (i=max(1,l1-l2+2);i<=l1;i++)
    {
        flag=1;
        for (j=1;i+j-1<=l1;j++)
          if (s2[j]!=s1[i+j-1])
          {
            flag=0;
            break;
          }
        if (flag) return l1-i+1;
    }
    return 0;
}
void pre()
{
    int i,j,x,y;
    n1=0;
    memset(out,0,sizeof(out));
    for (i=1;i<=n;i++)
      for (j=1;j<=n;j++)
        if (i!=j&&(inc(a[i].s[0],a[i].l,a[j].s[0],a[j].l)||inc(a[i].s[0],a[i].l,a[j].s[1],a[j].l))&&(!out[j]))
          out[i]=1;
    for (i=1;i<=n;i++)
      if (!out[i])
        f[n1++]=a[i];
    for (i=0;i<n1;i++)
      for (x=0;x<2;x++)
        for (j=0;j<n1;j++)
          for (y=0;y<2;y++)
            ovl[i][x][j][y]=cal(f[i].s[x],f[i].l,f[j].s[y],f[j].l);
}
void upd(int &x,int y)
{
    if (y>x) x=y;
}
void solve()
{
    int i,j,x,y,mx=(1<<n1)-1,tot=0,k,ans;
    memset(dp,128,sizeof(dp));
    dp[1][0][0]=0;
    for (i=1;i<mx;i++)
      for (j=0;j<n1;j++)
        if (i&(1<<j))
          for (x=0;x<2;x++)
            for (k=0;k<n1;k++)
              if (!(i&(1<<k)))
                for (y=0;y<2;y++)
                  upd(dp[i|(1<<k)][k][y],dp[i][j][x]+ovl[j][x][k][y]);
    for (i=0;i<n1;i++)
      tot+=f[i].l;
    ans=-1;
    for (i=0;i<n1;i++)
      for (x=0;x<2;x++)
        upd(ans,dp[mx][i][x]+ovl[i][x][0][0]);
    printf("%d\n",max(2,tot-ans));
}
int main()
{
    while (scanf("%d",&n)&&n)
    {
        in();
        pre();
        solve();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值