UVA 1204 Fun Game 状态压缩 圆圈的处理

题意:一群小孩(至少两个)围成一圈做游戏。每一轮从某个小孩开始往他左边或者右边传手帕。一个小孩拿到手帕后在手帕上写上自己的性别,男孩写B,女孩写G,然后按照相同的方向传递给下一个小孩,每一轮都可能在任何一个小孩写完后停止,现在游戏已经进行了n轮,已知n轮中每轮手帕上留下的字,问最少可能有几个小孩。

分析:预处理去掉所有的被其他串包含的串,然后处理出所有串的重叠长度。状态的转移就很好写了嘛,考虑当前一个没有拿到的串,假设为j,正反状态为y(j要求没有拿过,即i&(1<<j)为0),当前拿到的串的集合为s且当前最后一次的串为i,i的状态是x

dp[s|(1<<j)][j][y]=dp[s][i][x]+length[j]-overlap[i][j][x][y]

length[j]代表第j个字符串的长度,overlap[i][j][x][y]代表正反状态为x的第i字符串和正反状态为y的第j字符串的重叠长度(i左j右),最后还要处理结尾的串和起始串的重合部分,得出最终的结果。注意小孩子的个数最少为两个人。

LRJ代码:

#include<bits/stdc++.h>
using namespace std;

#define REP(i,n) for(int i = 0; i < (n); i++)

const int maxn = 16;
const int maxlen = 100 + 5;

// the overlap length of a (left) and b (right) 求两个串的重叠长度
int calc_overlap(const string& a, const string& b)
{
    int n1 = a.length();
    int n2 = b.length();
    for(int i = 1; i < n1; i++)   // place b at position i (从i开始往后判断a是否与b相等)
    {
        if(n2 + i <= n1) continue; // b cannot extend to the right of a
        bool ok = true;
        for(int j = 0; i + j < n1; j++)
            if(a[i+j] != b[j])
            {
                ok = false;
                break;
            }
        if(ok) return n1 - i;
    }
    return 0;
}

struct Item
{
    string s, rev;
    bool operator < (const Item& rhs) const
    {
        return s.length() < rhs.s.length();
    }
};

int n;
string s[maxn][2];
int len[maxn];
int overlap[maxn][maxn][2][2];

void init()
{
    // read input
    Item tmp[maxn];
    REP(i,n)
    {
        cin >> tmp[i].s;
        tmp[i].rev = tmp[i].s;
        reverse(tmp[i].rev.begin(), tmp[i].rev.end());
    }
    // remove strings that are occurred in another string
    // 预处理,将包含于其他字符串的字串删除
    int n2 = 0;
    sort(tmp, tmp+n);
    REP(i,n)
    {
        bool need = true;
        for(int j = i+1; j < n; j++)
        {
            if(tmp[j].s.find(tmp[i].s) != string::npos ||
                    tmp[j].rev.find(tmp[i].s) != string::npos)
            {
                need = false;
                break;
            }
        }
        if(need) // 如果不是重复就加入数组
        {
            s[n2][0] = tmp[i].s;
            s[n2][1] = tmp[i].rev;
            len[n2] = tmp[i].s.length();
            n2++;
        }
    }
    n = n2;
    // calculate overlaps
    REP(i,n) REP(j,n) REP(x,2) REP(y,2)
    overlap[i][j][x][y] = calc_overlap(s[i][x], s[j][y]);
}

// d[s][i][x] is the minimal total length if we used set s and the last string is s[i][x]
int d[1<<maxn][maxn][2];

inline void update(int& x, int v)
{
    if(x < 0 || v < x) x = v;
}

void solve()
{
    // dp
    memset(d, -1, sizeof(d));
    d[1][0][0] = len[0]; // always use string s[0][0] first
    int full = (1<<n) - 1;
    for(int s = 1; s < full; s++)
    {
        // 枚举尾部
        REP(i,n) REP(x,2) if(d[s][i][x] >= 0)
            for(int j = 1; j < n; j++) // place j
                if(!(s & (1<<j))) //枚举新加入的串
                    REP(y,2) update(d[s|(1<<j)][j][y], d[s][i][x]+len[j]-overlap[i][j][x][y]); // 更新最小值
    }
    // find answer
    int ans = -1;
    REP(i,n) REP(x,2)
    {
        if(d[full][i][x] < 0) continue;
        update(ans, d[full][i][x]-overlap[i][0][x][0]); //len[0]一开始就加过了
    }
    if(ans <= 1) ans = 2; // problem said: at least two children
    cout << ans << "\n";
}

int main()
{
    while(cin >> n && n)
    {
        init();
        solve();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/tianwei0822/article/details/94627237