UVA_11125
一开始被这个题目的3000组数据吓到了,但后来真正仔细分析之后,才发现复杂度主要不是取决于这3000组数据,因此3000这个数只是唬人的。
看到题目之后,我们发现放石子可以看成一个阶段一个阶段地去放,每次放一组即可,只不过放的时候有些条件限制,那么我们可不可以分阶段进行dp计数呢?
带着这个疑问我们继续分析题目,发现取石子的过程并没有明确的先后顺序,所以我们不能人为规定一个取的顺序,因此每次取一组石子的这个过程只能用枚举去实现。为了能够枚举所取的石子,我们必然在每个阶段都要得知每类还剩多少石子,这样我们就不妨把剩余的每类石子的数量作为一个状态,但这个状态好像比较麻烦,但没关系,仔细想一下就会发现由于石子数比较少,我们可以把这个状态压缩成一个8进制的数,这样最多有8*8*8*8个状态嘛,很少的。
状态已经初步表示完了,但我们发现有个问题没有解决,我们怎么让取的时候避免和前面一组同颜色或者同数量呢?再把状态细化呗,多加两个整数pc、pn吧,一个记录前面石子的颜色,一个记录前面那组石子的数量,这样就好了嘛。但第一组石子呢?也就是说我们最初的时候把这个pc和pn初始化成什么呢?等等,好像还有一个问题我们也没解决,题目还要求首和尾也不能同颜色同数量呀。
但依然没关系,我们就把这两个问题一起解决。保证首尾不同确实不太好处理,但凡是不好处理的我们就偷懒嘛,怎么偷懒?枚举呗。如果我们指定了首或者尾那一组的状态,问题就都解决了。我们不妨指定尾的状态,颜色记为fc,数量记为fn,把两个整数也压缩进dp的状态吧。这样我们也就自然知道pc和pn一开始应该初始化成fc和fn了,于是只要后面取的过程保证不和pc同颜色,且不和pn同数量,自然就保证了首尾也是不同颜色、不同数量的。
现在似乎没什么太多的问题了,剩下的就是看看复杂度能不能承受了,如果不能我们就得想办法优化dp。总的状态4*3*4*3*8*8*8*8,每个状态最多转移4*3次,一共是7077888,时间是没问题的。
哦对,还有那3000组数据,其实我们刚才算的复杂度是包含了所有的状态的,只要我们一直是保持记忆化搜索的状态的话,数据的组数是没关系的,我们依旧不会重复去算一些状态,只不过增加了查询的次数而已。
空间上为了节省一点,前面4个状态我们用4进制,后面4个状态我们用8进制,把8个数压缩成一个整数。
#include<stdio.h>
#include<string.h>
#define MAXD 1100000
int f[MAXD], a[5], b[5];
int dp(int state)
{
int i, j, k, t, st = state, ans = 0, fc, fn, pc, pn;
if(f[state] != -1)
return f[state];
fc = st % 4, st /= 4;
fn = st % 4, st /= 4;
pc = st % 4, st /= 4;
pn = st % 4, st /= 4;
k = 0;
for(i = 0; i < 4; i ++)
{
b[i] = st % 8, st /= 8;
k += b[i];
}
if(k == 0)
{
if(pc == fc && pn == fn)
return f[state] = 1;
else
return f[state] = 0;
}
for(i = 0; i < 4; i ++)
{
if(i == pc)
continue;
for(j = 1; j <= 3 && j <= b[i]; j ++)
{
if(j == pn)
continue;
b[i] -= j;
for(k = 3, t = 0; k >= 0; k --)
t = t * 8 + b[k];
t = (((t * 4 + j) * 4 + i) * 4 + fn) * 4 + fc;
ans += dp(t);
b[i] += j;
}
}
return f[state] = ans;
}
void solve()
{
int i, j, k, n, res, t;
memset(a, 0, sizeof(a));
scanf("%d", &n);
k = 0;
for(i = 0; i < n; i ++)
{
scanf("%d", &a[i]);
k += a[i];
}
if(!k)
{
printf("1\n");
return ;
}
for(i = 3, k = 0; i >= 0; i --)
k = k * 8 + a[i];
res = 0;
for(i = 0; i < n; i ++)
for(j = 1; j <= 3 && j <= a[i]; j ++)
{
t = (((k * 4 + j) * 4 + i) * 4 + j) * 4 + i;
res += dp(t);
}
printf("%d\n", res);
}
int main()
{
int t;
memset(f, -1, sizeof(f));
scanf("%d", &t);
while(t --)
solve();
return 0;
}