sence:
题目大意:一个目标串k,模式串集合S,现在两人轮流从k的头或尾删去一个字符,在某一次操作后k' ∈ S,则操作者胜出。问先手者是否有必胜策略。
解法:观察,进行递推:
k = abc, sg = 0;
k = xabc || k = abcx sg = 1;
k = xabcx, sg = 0;
k = xabcxx || k = xxabcx; sg = 1;
k = ...xabc.. || k = ..abcx.. [len(...x) > len(..)]; sg = 0;
......
所以当且仅当s匹配k且s在k正中央时(s ∈ S),存在必胜策略(当时没想到这一点,用kmp进行匹配,计算了所有可能的匹配方案,而其中大部分是可以直接判断不合法的,浪费了时间,所以TLE)。
对于一个s,如果 len(k) - len(s) 为偶数,则一定不存在必胜策略;
否则从k[(len(k) - len(s)) / 2]以及k[(len(k) - len(s)) / 2 + 1]开始匹配,如果s匹配k,则存在必胜策略;
否则不存在。
算法复杂度为线性。
inline bool check(void)
{
int p = klen = slen = 0;
memset(k, 0, sizeof(k));
memset(s, 0, sizeof(s));
scanf("%s%d", k, &p);
klen = strlen(k);
for (int i = 1; i <= p; ++i)
{
scanf("%s", s);
slen = strlen(s);
if (slen > klen) continue;
if (((!(klen & 1)) && (slen & 1)) || ((klen & 1) && (!(slen & 1))))
{
int v = (klen - slen) >> 1;
bool flag = 1;
for (int j = 0; j < slen; ++j)
{
if (k[v + j] != s[j])
{
flag = 0;
break;
}
}
if (flag)
{
for (int j = i + 1; j <= p; j ++) scanf("%s", s);
return 1;
}
v ++;
flag = 1;
for (int j = 0; j < slen; ++j)
{
if (k[v + j] != s[j])
{
flag = 0;
break;
}
}
if (flag)
{
for (int j = i + 1; j <= p; ++j) scanf("%s", s);
return 1;
}
<pre name="code" class="cpp"> }
}
return 0;
}
tank:
题目大意:已知m个整数,整数x(1≤x≤1023);在这m个整数中取n个数出来,与x按位取与值为0。求n的期望值。
解法:记忆化搜索推导dp方程,类似于01背包,f[i][j][k]当前选到第i个数,已经选了j个数,x的值为k时,当前状态对答案的贡献。
第一维可以滚动数组滚掉;转移方程: f[j + 1][k & v[i]] += f[j][k] / (m - j),注意f[j][k] > 0。
ans = Σ(i = 1, i ≤ n)((Π(1, i) - Π(1, i - 1)) * i)。
for (int i = 0; i < m; ++ i)
{
for (int j = i; j >= 0; -- j)
{
for (int k = 0; k < 1024; ++ k)
{
if (f[j][k] > 0) f[j + 1][k & v[i]] += f[j][k] / (double)(m - j);
}
}
}
int ans = 0;
for (int i = 1; i <= n; ++i)
{
double sc(f[i][0]), sp(f[i - 1][0]);
for (int j = 1; j < i; ++j) sp *= j;
for (int j = 1; j <= i; ++j) sc *= j;
ans += (sc - sp) * i;
}
terroris:
题目大意:给定一个串长为k,一个空串,有4种操作:
1,让当前串变为k的n长前缀,花费n * A;
2.花费B使当前串倍增;
3,花费C删去当前串任意长度后缀;
4,花费D在当前串末尾加上一个字符。
问最小花费
解法:记录当前串hash值,当前操作,当前操作数,然后记忆化搜索或者dp;
dp[i][j] = min(dp[i->pre()][1~4]);