One day Greg and his friends were walking in the forest. Overall there were n people walking, including Greg. Soon he found himself in front of a river. The guys immediately decided to get across the river. Luckily, there was a boat by the river bank, just where the guys were standing. We know that the boat can hold people with the total weight of at most k kilograms.

Greg immediately took a piece of paper and listed there the weights of all people in his group (including himself). It turned out that each person weights either 50 or 100 kilograms. Now Greg wants to know what minimum number of times the boat needs to cross the river to transport the whole group to the other bank. The boat needs at least one person to navigate it from one bank to the other. As the boat crosses the river, it can have any non-zero number of passengers as long as their total weight doesn't exceed k.

Also Greg is wondering, how many ways there are to transport everybody to the other side in the minimum number of boat rides. Two ways are considered distinct if during some ride they have distinct sets of people on the boat.

Help Greg with this problem.

Input

The first line contains two integers nk (1 ≤ n ≤ 50, 1 ≤ k ≤ 5000) — the number of people, including Greg, and the boat's weight limit. The next line contains n integers — the people's weights. A person's weight is either 50 kilos or 100 kilos.

You can consider Greg and his friends indexed in some way.

Output

In the first line print an integer — the minimum number of rides. If transporting everyone to the other bank is impossible, print an integer-1.

In the second line print the remainder after dividing the number of ways to transport the people in the minimum number of rides by number 1000000007 (109 + 7). If transporting everyone to the other bank is impossible, print integer 0.

题目大意:n个人,有人的体重是50,有的人体重是100,船只只能承受k的重量。现在只有一条船,问最少须要渡河多少次,才能把这n个人都送到对岸(渡河时船上不能没人),在最小次数下问有多少种方案渡河,同一体重的两个人视为不同的人。

思路:设dis[x][y][boat]代表原来的岸上有x个体重为50的人,y个体重为100的人,有一条船在原来的岸上(其实boat不是0就是1),到结束状态dis[0][0][0]的最短距离。从状态dis[0][0][0]开始做最短路,每一步代价为1,故使用BFS,转移的时候只需要注意船上至少有一个人,重量不要超过k就好了。

那么dis[][][]数组求好了,从起始状态dis[n1][n2][1]到最终状态的最少渡河次数就能求出来了,其中n1代表原来有n1个体重为50的人,n2为原来用n2个体重为100的人。

然后求次数,dp就可以了。状态类似用dp[x][y][boat]表示到达某个状态的方案,对于下一个方案dp[i][j][!boat],当且仅当dis[i][j][!boat] + 1 = dis[x][y][boat],船不为空,不超载时转移。

dp[i][j][!boat] += dp[x][y][boat] * (可以移动的体重为50的人选需要移动的体重为50的人的方案数) * (可以移动的体重为100的人选需要移动的体重为100的人的方案数)

复杂度为O(n^4)

不过实际上可以在求dis的时候直接倒着把dp也算了……

代码(30MS):

 #include <cstring>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII; const int MAXN = ;
const int INF = 0x3f3f3f3f;
const int MOD = ; struct State {
int x, y, boat;
State() {}
State(int x, int y, int boat):
x(x), y(y), boat(boat) {}
}; int dis[MAXN][MAXN][], vis[MAXN][MAXN][];
LL dp[MAXN][MAXN][], c[MAXN][MAXN];
int n, k, n1, n2; void init() {
c[][] = ;
for(int i = ; i <= ; ++i) {
c[i][] = ;
for(int j = ; j <= i; ++j) c[i][j] = (c[i - ][j] + c[i - ][j - ]) % MOD;
}
//int a, b; while(cin>>a>>b) cout<<c[a][b]<<endl;
} void bfs() {
memset(dis, 0x3f, sizeof(dis));
queue<State> que; que.push(State(, , ));
dis[][][] = ;
while(!que.empty()) {
State p = que.front(); que.pop();
if(p.boat == )
for(int i = p.x; i <= n1; ++i)
for(int j = p.y; j <= n2; ++j) {
if(i == p.x && j == p.y) continue;
if(dis[i][j][] != INF || (i - p.x) + * (j - p.y) > k) continue;
dis[i][j][] = dis[p.x][p.y][] + ;
que.push(State(i, j, ));
}
else
for(int i = ; i <= p.x; ++i)
for(int j = ; j <= p.y; ++j) {
if(i == p.x && j == p.y) continue;
if(dis[i][j][] != INF || (p.x - i) + * (p.y - j) > k) continue;
dis[i][j][] = dis[p.x][p.y][] + ;
que.push(State(i, j, ));
}
}
} LL solve() {
if(dis[n1][n2][] == INF) {
puts("-1");
return ;
}
queue<State> que; que.push(State(n1, n2, ));
dp[n1][n2][] = ;
while(!que.empty()) {
State p = que.front(); que.pop();
if(p.boat == )
for(int i = ; i <= p.x; ++i)
for(int j = ; j <= p.y; ++j) {
if(i == p.x && j == p.y) continue;
if((p.x - i) + * (p.y - j) > k) continue;
if(dis[i][j][] + != dis[p.x][p.y][]) continue;
dp[i][j][] = (dp[i][j][] + dp[p.x][p.y][] * (c[p.x][p.x - i] * c[p.y][p.y - j]) % MOD) % MOD;
if(!vis[i][j][]) {
vis[i][j][] = true;
que.push(State(i, j, ));
}
}
else
for(int i = p.x; i <= n1; ++i)
for(int j = p.y; j <= n2; ++j) {
if(i == p.x && j == p.y) continue;
if((i - p.x) + * (j - p.y) > k) continue;
if(dis[i][j][] + != dis[p.x][p.y][]) continue;
dp[i][j][] = (dp[i][j][] + dp[p.x][p.y][] * (c[n1 - p.x][i - p.x] * c[n2 - p.y][j - p.y]) % MOD) % MOD;
if(!vis[i][j][]) {
vis[i][j][] = true;
que.push(State(i, j, ));
}
}
}
cout<<dis[n1][n2][]<<endl;
return dp[][][];
} int main() {
init();
scanf("%d%d", &n, &k);
k /= ;
for(int i = ; i <= n; ++i) {
int x;
scanf("%d", &x);
n1 += (x == );
}
n2 = n - n1;
bfs();
cout<<solve()<<endl;
}

codeforces 295C Greg and Friends(BFS+DP)的更多相关文章

  1. Codeforces Gym101201B:Buggy Robot(BFS + DP)

    题目链接 题意 给出一个n*m的地图,还有一个操作序列,你原本是要按照序列执行操作的,但是你可以修改操作:删除某些操作或者增加某些操作,问从'R'到'E'最少需要多少次修改操作. 思路 和上次比赛做的 ...

  2. ZOJ 3596Digit Number(BFS+DP)

    一道比较不错的BFS+DP题目 题意很简单,就是问一个刚好包含m(m<=10)个不同数字的n的最小倍数. 很明显如果直接枚举每一位是什么这样的话显然复杂度是没有上限的,所以需要找到一个状态表示方 ...

  3. CodeForces - 1073E :Segment Sum (数位DP)

    You are given two integers l l and r r (l≤r l≤r ). Your task is to calculate the sum of numbers from ...

  4. 【2019.8.14 慈溪模拟赛 T1】我不是!我没有!别瞎说啊!(notme)(BFS+DP)

    \(IDA^*\) 说实话,这道题我一开始没想出正解,于是写了一个\(IDA^*\)... 但神奇的是,这个\(IDA^*\)居然连字符串长度分别为\(2500,4000\)的数据都跑得飞快,不过数据 ...

  5. codeforces 591 E. Three States(bfs+思维)

    题目链接:http://codeforces.com/contest/591/problem/E 题意:有3个数字表示3个城市,每种城市都是相互连通的,然后不同种的城市不一定联通,'.'表示可以建设道 ...

  6. Codeforces 633F - The Chocolate Spree(树形 dp)

    Codeforces 题目传送门 & 洛谷题目传送门 看来我这个蒟蒻现在也只配刷刷 *2600 左右的题了/dk 这里提供一个奇奇怪怪的大常数做法. 首先还是考虑分析"两条不相交路径 ...

  7. CodeForces 690C2 Brain Network (medium)(树上DP)

    题意:给定一棵树中,让你计算它的直径,也就是两点间的最大距离. 析:就是一个树上DP,用两次BFS或都一次DFS就可以搞定.但两次的时间是一样的. 代码如下: #include<bits/std ...

  8. Codeforces 758D Ability To Convert(区间DP)

    题目链接:http://codeforces.com/problemset/problem/758/D 题意:一个n进制下的数k,其中k不会用字母,如果有A就用10代替了.求k这个数对应的,在10进制 ...

  9. codeforces 633F The Chocolate Spree (树形dp)

    题目链接:http://codeforces.com/problemset/problem/633/F 题解:看起来很像是树形dp其实就是单纯的树上递归,就是挺难想到的. 显然要求最优解肯定是取最大的 ...

随机推荐

  1. 获取当前对象的key的名称

    获取当前对象的key的名称(无法获取),只能曲线救国 通过给标签添加class,id, 然后通过对class的遍历,来获取到id(这个id对应数据库的字段,所以对应对象的key) 然后再给 id 赋值 ...

  2. 初识Pentaho(一)

    学习一门语言或工具,首先得知道该工具的用途是什么.Pentaho 的官方定义是一个集数据集成和数据分析于一体的平台.这样的解释还是有点模糊.还是看其有哪些特点吧:  ☐可以进行数据集成.谈到数据集成这 ...

  3. 【每天一条Linux指令-Day1】kill掉多个mysql的进程

    我被问到过一个shell的问题,问的是如何kill掉多个mysql的进程? 怎么把这个的pid传递下去 ps -ef|grep mysql | xargs kill -9 ps -ef|grep my ...

  4. A1092

    可输入内容为0-9,a-z,A-Z. 输入: 第一行输入任意字符串: 第二行输入期望字符串. 输出: 如果第一行包含了所有期望字符串,输出yes和多余字符个数: 如果第一行不能完全包含期望字符串,输出 ...

  5. C# 调用腾讯云接口获取视频基本信息

    做项目需要上传视频,获取时长,上传教程很多,获取信息很少,官方只有一条请求地址. 找了好久,都没有说这个请求地址怎么用.最后发现需要调用腾讯云SDK 官方地址:https://github.com/Q ...

  6. JS本地保存数据的几种方法

    1.Cookie 这个恐怕是最常见也是用得最多的技术了,也是比较古老的技术了.COOKIE优点很多,使用起来很方便 但它的缺点也很多: 比如跨域访问问题:无法保存太大的数据(最大仅为4KB):本地保存 ...

  7. Understanding Delegated JavaScript Events

    While I ended up using a CSS-only implementation for this pen, I started by writing it mostly using ...

  8. linux如何制作程序桌面快捷方式

    1.生成通过apt或者dpkg安装的程序的桌面快捷方式 他们的快捷方式在/usr/share/applications中,比如我们要生成火狐的桌面快捷方式,执行下列命令 cp /usr/share/a ...

  9. Prism for WPF 搭建一个简单的模块化开发框架(五)添加聊天、消息模块

    原文:Prism for WPF 搭建一个简单的模块化开发框架(五)添加聊天.消息模块 中秋节假期没事继续搞了搞 做了各聊天的模块,需要继续优化 第一步画页面 页面参考https://github.c ...

  10. Prism for WPF 搭建一个简单的模块化开发框架(三) 给TreeView加样式做成菜单

    原文:Prism for WPF 搭建一个简单的模块化开发框架(三) 给TreeView加样式做成菜单 昨天晚上把TreeView的样式做了一下,今天给TreeView绑了数据,实现了切换页面功能 上 ...