这几天忽然想起了cqx的一道dp题,似乎很难。反正也闲来无事,于是就做了做。然后,完全被虐了。。
题目来源于shtsc2010,舞会。题目大意为,给定n个男生身高为B[i],n个女生身高为G[i],求有多少种搭配方式,使得至多k对舞伴之间,女生比男生高。
cqx有两种dp方式,我是按第一种方式来dp的。
现将B[i],G[i]升序排序,定义状态f[i,j]为男女各前i个人,女生刚好比男生高j的舞伴对数恰好为k的方案数。
令Gk = min {k|1 <= k <= i, G[Gk] > B[i]},Bk = min {k|1 <= k <= i, B[Bk] >= G[i]},注意一个有等号一个没有。
先写转移方程:f[i][j] = f[i - 1][j - (B[i] < G[i])] + (i - j + Bk - Gk) * f[i -1][j - 1] + (j - Bk + Gk) * f[i - 1][j]。看不懂?没关系,这个题难就难在转移,这转移都难成一种境界了。
推了一晚上的转移,总是在几个量中徘徊。我都快疯掉了。。
今天早上一来,接着推转移,终于,被我AC掉了。
转移是这样:
1. 将B[i]与G[i]配对;
这个时候原来的共有j - (B[i] < G[i])对,所以从f[i - 1][j - (B[i] < G[i])] 转移过来。
2. 令B[k] > G[i],由G以及B的单调性得出,B[i] > B[k] > G[i] > G[k],若将B[k]与B[i]互换,则互换之后共有0对使得女生大于男生,则此时f[i][j]由f[i - 1][j]转移过来,由于k共有i - Bk个,所以f[i][j]须加上(i - Bk) * f[i - 1][j]。
3. 令G[k] > B[i],同理可得,女生比男生高的对数由1->2,所以从f[i - 1][j - 1]转移过来,k共有i - Gk个。
4. 若B[k] < G[k] < B[i],则交换B[k]与B[i]会使女生比男生高的对数不变,所以从f[i - 1][j]转移过来,由于满足B[k] < G[k]的k共有j个,满足G[k] > B[i]的k共有i-Gk个,所以满足条件的k共有j - (i - Gk)个。
5. 若G[k] < B[k] < G[i],则交换B[k]与B[i]会使对数增加1,所以从f[i - 1][j - 1]转移过来,同理可得,满足条件的k共有(i - j) - (i - Bk)个。
综上所述,全部加起来,即得f[i][j]的转移表达式。
由于cqx太缺德,还要写高精(其实是为了卡住第二种方法),所以写的头昏脑胀,四肢无力,烦死了。。
最后,照例贴上代码:
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#define maxlen 100
#define maxn 205
#define base 1000000
typedef int bn[maxlen];
bn buf;
bn dp[maxn][maxn];
int B[maxn];
int G[maxn];
void add (bn p, bn q, int k)
{
if (!k || !q[0] || !q[q[0]]) return;
bn x;
int i;
memset (x, 0, sizeof (x));
x[0] = p[0] > q[0] ? p[0] : q[0];
for (i = 1; i <= x[0]; ++i)
x[i] = p[i] + q[i] * k;
for (i = 1; i <= x[0]; ++i)
if (x[i] >= base)
x[i + 1] += x[i] / base, x[i] %= base;
if (x[x[0] + 1]) ++x[0];
memcpy (p, x, sizeof (bn));
}
int cmper (const void *i, const void *j)
{
return *(int *)i - *(int *)j;
}
int main()
{
FILE *fin = fopen ("ball.in" , "r");
FILE *fout = fopen ("ball.out", "w");
int n, k, Gk = 0, Bk = 0, i, j;
bn ans;
fscanf (fin, "%d%d", &n, &k);
for (i = 1; i <= n; ++i)
fscanf (fin, "%d", &B[i]);
for (i = 1; i <= n; ++i)
fscanf (fin, "%d", &G[i]);
qsort (B + 1, n, sizeof (B[0]), cmper);
qsort (G + 1, n, sizeof (G[0]), cmper);
dp[0][0][0] = 1, dp[0][0][1] = 1;
for (i = 1; i <= n; ++i)
{
for (; G[Gk] <= B[i] && Gk < i; ++Gk);
for (; B[Bk] < G[i] && Bk < i; ++Bk);
for (j = i - Gk; j <= Bk; ++j)
{
memcpy (dp[i][j], dp[i - 1][j - (B[i] < G[i])], sizeof (bn));
add (dp[i][j], dp[i - 1][j - 1], i - j + Bk - Gk);
add (dp[i][j], dp[i - 1][j ], j - Bk + Gk);
}
}
memset (ans, 0, sizeof (ans));
for (i = 0; i <= k; ++i)
add (ans, dp[n][i], 1);
fprintf (fout, "%d", ans[ans[0]]);
for (; --ans[0] > 0; )
fprintf (fout, "%06d", ans[ans[0]]);
fclose (fin);
fclose (fout);
return 0;
}