CodeForces 295C Greg and Friends

bfs + dp 好题。
真好,比赛的时候想不出来,看题解的时候感觉好复杂……
看完写的时候感觉非常流畅!

题意:
载人过岸,一艘船最大载重 k 已经给定。每个人的重量只会是50或者100。问最小的过岸次数与方案数。

思路:
老实说这个题目我可以看就知道是道搜索题,因为和那个什么东西(就是搜索入门的那个)来着非常像。
最重要的还是理清状态与思路。
以左岸50重人数,100重人数,船是否在左岸表示一个状态,对他进行 bfs ,只要满足船重量大于 0 并且小于等于最大 k,每次过岸花费 1 。第一个搜索到 最终状态的就是我们要求的结果。

而我们的方案数,只能通过dp来球,dp最讲求的就是状态,

转移方程如下:

如果是 左岸 — > 右岸 anum表示 50重总人数 , bnum 表示 100重 总人数

( dp[x][y][0] += dp[a][b][1] \times C_a^{a-x} \times C_b^{b-y} )

否则

( dp[x][y][1] += dp[a][b][0] \times C_{anum-a}^{x-a} \times C_{bnum-b}^{y-b} )

写到中间的时候突然发现组合数求不来……尴尬……百度了一下,在数据较小的时候可以直接递推求

( C_n^m = C_n^{m-1} + C_{n-1}^{m-1} )

AC Code

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

#define ll long long

const int maxn = 55;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;

int n, k;
int anum, bnum, maxa, maxb;
ll dp[maxn][maxn][2], c[maxn][maxn], dis[maxn][maxn][2];

struct status {
    int an;
    int bn;
    int isLeft;
    status() {}
    status(int _an, int _bn, int _isLeft)
        : an(_an)
        , bn(_bn)
        , isLeft(_isLeft)
    {
    }
};

queue<status> que;

int bfs()
{
    c[0][0] = 1;
    for (int i = 1; i <= 50; i++) {
        c[i][0] = 1;
        for (int j = 1; j <= i; ++j)
            c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
    }
    maxa = anum > 0 ? k / anum : 0, maxb = bnum > 0 ? k / bnum : 0;
    que.push(status(anum, bnum, 1));
    dis[anum][bnum][1] = 0;
    dp[anum][bnum][1] = 1;
    status now, nxt;
    while (!que.empty()) {
        now = que.front();
        que.pop();
        if (!now.an && !now.bn && !now.isLeft)
            return dis[0][0][0];
        for (int i = 0; i <= maxb; i++)
            for (int j = i ? 0 : 1; j <= maxa; j++) {
                if (i * 100 + j * 50 > k)
                    break;
                if (now.isLeft) {
                    if (now.an - j < 0 || now.bn - i < 0)
                        continue;
                    nxt.an = now.an - j, nxt.bn = now.bn - i, nxt.isLeft = false;
                    (dp[nxt.an][nxt.bn][0] += dp[now.an][now.bn][1] * c[now.an][j] % mod * c[now.bn][i] % mod) %= mod;
                    if (!dis[nxt.an][nxt.bn][0]) {
                        dis[nxt.an][nxt.bn][0] = dis[now.an][now.bn][1] + 1;
                        que.push(nxt);
                    }
                } else {
                    if (now.an + j > anum || now.bn + i > bnum)
                        continue;
                    nxt.an = now.an + j, nxt.bn = now.bn + i, nxt.isLeft = true;
                    (dp[nxt.an][nxt.bn][1] += dp[now.an][now.bn][0] * c[anum - now.an][j] % mod * c[bnum - now.bn][i] % mod) %= mod;
                    if (!dis[nxt.an][nxt.bn][1]) {
                        dis[nxt.an][nxt.bn][1] = dis[now.an][now.bn][0] + 1;
                        que.push(nxt);
                    }
                }
            }
    }
    return -1;
}

int main()
{
    int weight;
    scanf("%d %d", &n, &k);
    for (int i = 0; i < n; i++) {
        scanf("%d", &weight);
        if (weight == 50)
            anum++;
        else
            bnum++;
    }
    int step = bfs();
    if (step == -1) {
        puts("-1\n0");
    } else
        printf("%d\n%lld\n", step, dp[0][0][0]);
    return 0;
}