题解 CF295C 【Greg and Friends】
hello_world2005 · · 题解
三维DP
dp[i][j][k]表示第i次运送后对岸有j个50kg,k个100kg的方案数
若经过2*n次运送后则必为无解(原因自己想)
题意:
有一个船最多能载K千克, 现在有n个人要过河, 船只有一条,每个人要么是50千克,要么是100千克,问有多少种方案可以到达河对岸,且来回次数最少? 先输出最少来回次数,再输出方案数,结果对10^9+7取模 无解输出 -1 0
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
long long n, k;
long long a[55];
long long c[55][55];
long long dp[105][55][55];
/*int会炸!!!!!*/
long long num_50, num_100;
long long old, now;
bool ok;
int main() {
for(int i = 0; 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;
}
scanf("%lld%lld", &n, &k);
k /= 50;
for(int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
if(a[i] == 50)
num_50++;
else
num_100++;
}
old = 1;
now = 0;
dp[0][num_50][num_100] = 1;
for(int l = 0; l < 2 * n; l++) {
/*从这岸到那岸*/
swap(now, old);
memset(dp[now], 0, sizeof(dp[now]));
for(int i = 0; i <= num_50; i++) {
for(int j = 0; j <= num_100; j++) {
if(i + j * 2 && (i + j * 2) <= k) {
for(int x = i; x <= num_50; x++) {
for(int y = j; y <= num_100; y++) {
dp[now][num_50 - x + i][num_100 - y + j] += dp[old][x][y] * c[x][i] % MOD * c[y][j] % MOD;
}
}
}
}
}
if(dp[now][num_50][num_100]) {
printf("%d\n", l * 2 + 1);
printf("%lld\n", dp[now][num_50][num_100] % MOD);
ok = 1;
break;
//输出解
}
/*从那岸回来*/
swap(now, old);
memset(dp[now], 0, sizeof(dp[now]));
for(int i = 0; i <= num_50; i++) {
for(int j = 0; j <= num_100; j++) {
if(i + j * 2 && (i + j * 2) <= k) {
for(int x = i; x <= num_50; x++) {
for(int y = j; y <= num_100; y++) {
dp[now][num_50 - x + i][num_100 - y + j] += dp[old][x][y] * c[x][i] % MOD * c[y][j] % MOD;
}
}
}
}
}
}
if(ok == 0)
printf("-1\n0");
//无解
return 0;
}