本周讲了图论中最短路以及最小生成树MST的基本算法~ 也介绍了图论中一类的特殊的问题“差分约束”~ 可以通过Bellman-Ford求解~
本周练习题题解如下:
Pro A
题意:求次短路
具体思路: 有点dp的意味 再开一个数组在更新最短路的时候 也同时更新次短路
也要保证次短路一定比最短路短 具体细节见code
参考code:
//
// POJ 3255 Roadblocks
//
// Created by TaoSama on 2015-03-20
// Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
#define CLR(x,y) memset(x, y, sizeof(x))
using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
int n, r, dp[5005], dp2[5005];
struct Edge {
int to, cost;
};
vector<Edge> G[N];
typedef pair<int, int> Sta; //距离, 节点标号
int dijkstra() {
priority_queue<Sta, vector<Sta>, greater<Sta> > pq;
memset(dp, 0x3f, sizeof dp); memset(dp2, 0x3f, sizeof dp2);
dp[1] = 0; pq.push(Sta(0, 1));
while(!pq.empty()) {
Sta p = pq.top(); pq.pop();
int u = p.second, d = p.first;
if(d > dp2[u]) continue;
for(int i = 0; i < G[u].size(); ++i) {
Edge &e = G[u][i];
int d2 = d + e.cost;
if(dp[e.to] > d2) {
swap(dp[e.to], d2);
pq.push(Sta(dp[e.to], e.to));
}
if(dp2[e.to] > d2 && dp[e.to] < d2) {
dp2[e.to] = d2;
pq.push(Sta(dp2[e.to], e.to));
}
}
}
return dp2[n];
}
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
//ios_base::sync_with_stdio(0);
cin >> n >> r;
for(int i = 1; i <= r; ++i) {
int x, y, v; cin >> x >> y >> v;
G[x].push_back((Edge) {y, v});
G[y].push_back((Edge) {x, v});
}
cout << dijkstra() << endl;
return 0;
}
Pro B
题意:求牛1-N 其实最最大距离就是1-N的满足条件的最短路
具体思路:
由最短路性质可知 对于所有的边(u, v) 均有d[u] + cost[u][v] >= d[v]
那么由题目已知 必须顺序排序 但是可以多牛共存一点
1. 即 d[i+1] >= d[i] → d[i+1] + 0 >= d[i] (从i+1连一条权为0的边到i)
2. 喜欢的牛 B - A <= D → A + D >= B (从A连一条权为D的边到B)
3. 讨厌的牛 B - A >= D → B - D >= A (从B连一条权为-D的边到A)
由于存在负权 我们可以用Bellman-Ford来算最短路 O(EV)的复杂度题目规模完全够
参考code:
//
// POJ 3169 Layout
//
// Created by TaoSama on 2015-03-20
// Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
#define CLR(x,y) memset(x, y, sizeof(x))
using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
int n, l, d, dp[1005];
struct Edge {
int to, cost;
};
vector<Edge> G[21005];
int BellmanFord() {
memset(dp, 0x3f, sizeof dp); dp[1] = 0;
for(int k = 1; k <= n; ++k) {
for(int i = 1; i <= n; ++i) {
for(int j = 0; j < G[i].size(); ++j) {
Edge &e = G[i][j];
dp[e.to] = min(dp[e.to], dp[i] + e.cost);
//printf("dp[%d]: %d\n",e.to, dp[e.to]);
}
}
}
return dp[n];
}
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
scanf("%d%d%d", &n, &l, &d);
int a, b, c;
for(int i = 1; i < n; ++i)
G[i + 1].push_back((Edge) {i, 0});
for(int i = 1; i <= l; ++i) {
scanf("%d%d%d", &a, &b, &c);
G[a].push_back((Edge) {b, c});
}
for(int i = 1; i <= d; ++i) {
scanf("%d%d%d", &a, &b, &c);
G[b].push_back((Edge) {a, -c});
}
int ans = BellmanFord();
if(dp[1] < 0) ans = -1;
else if(ans == INF) ans = -2;
printf("%d\n", ans);
return 0;
}
Pro C
题意:求某牛到其他牛的最小平均度
具体思路: 多源最短路径 那么Floyd 并且300个点 完全可行 预处理参演同一场电影的牛互相之间度为1
传递的度为2 仔细想想本题牛之间的度 只有度1 度2 和没度 三种情况
参考code:
由于没有初始化dp[i][i] = 0 后面相加的时候跳过了i==j的情况 - - 但是你们别忘记了 不然你懂的
//
// POJ 2139 Six Degrees of Cowvin Bacon
//
// Created by TaoSama on 2015-03-20
// Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
#define CLR(x,y) memset(x, y, sizeof(x))
using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
int n, m, a[305], dp[305][305];
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
scanf("%d%d", &n, &m);
memset(dp, 0x3f, sizeof dp);
for(int i = 1; i <= m; ++i) {
int t; scanf("%d", &t);
for(int j = 1; j <= t; ++j) scanf("%d", a + j);
for(int j = 1; j <= t; ++j)
for(int k = j + 1; k <= t; ++k)
dp[a[j]][a[k]] = dp[a[k]][a[j]] = 1;
}
for(int k = 1; k <= n; ++k)
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
int ans = INF;
for(int i = 1; i <= n; ++i) {
int t = 0;
for(int j = 1; j <= n; ++j) {
if(i == j) continue;
t += dp[i][j];
}
ans = min(ans, t);
}
printf("%d\n", ans * 100 / (n - 1));
return 0;
}
Pro D
题意:
具体思路: 这里给出一种 把边权取反 Kruskal求最大生成森林即可
参考code:
//
// POJ 3723 Conscription
//
// Created by TaoSama on 2015-03-20
// Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
#define CLR(x,y) memset(x, y, sizeof(x))
using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
int n, m, r;
struct Edge {
int u, v, cost;
bool operator<(const Edge& rhs) const {
return cost < rhs.cost;
}
} G[50005];
int par[20005], rank[20005];
void init(int n) {
for(int i = 0; i < n; ++i) {
par[i] = i;
rank[i] = 0;
}
}
int find(int x) {
if(par[x] == x) return x;
return par[x] = find(par[x]);
}
void unite(int x, int y) {
x = find(x); y = find(y);
if(rank[x] < rank[y]) par[x] = y;
else {
par[y] = x;
if(rank[x] == rank[y]) ++rank[x];
}
}
bool same(int x, int y) {
return find(x) == find(y);
}
int kruskal() {
int ret = 0;
sort(G + 1, G + 1 + r);
init(n + m);
for(int i = 1; i <= r; ++i) {
Edge &e = G[i];
if(!same(e.u, e.v)) {
ret += e.cost;
unite(e.u, e.v);
}
}
return ret;
}
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
int t; scanf("%d", &t);
while(t--) {
scanf("%d%d%d", &n, &m, &r);
for(int i = 1; i <= r; ++i) {
int x, y, v; scanf("%d%d%d", &x, &y, &v);
G[i] = (Edge) {x, y + n, -v};
}
int ans = 10000 * (n + m) + kruskal();
printf("%d\n", ans);
}
return 0;
}
Pro E
题意:求最小生成树
具体思路: prim裸题没啥好说的 由于V^2条边 可以用邻接矩阵写
参考code:
//
// POJ 1258 Agri-Net
//
// Created by TaoSama on 2015-03-21
// Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
#define CLR(x,y) memset(x, y, sizeof(x))
using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
int n, a[105][105], minCost[105];
bool used[105];
typedef pair<int, int> Sta;
int prim() {
int ret = 0;
memset(minCost, 0x3f, sizeof minCost);
memset(used, false, sizeof used);
priority_queue<Sta, vector<Sta>, greater<Sta> > pq;
pq.push(Sta(0, 1)); minCost[1] = 0;
while(!pq.empty()) {
Sta p = pq.top(); pq.pop();
int u = p.second;
if(used[u] || p.first > minCost[u]) continue;
used[u] = true; ret += minCost[u];
for(int i = 1; i <= n; ++i) {
if(minCost[i] > a[u][i]) {
minCost[i] = a[u][i];
pq.push(Sta(minCost[i], i));
}
}
}
return ret;
}
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(scanf("%d", &n) == 1) {
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
scanf("%d", &a[i][j]);
printf("%d\n", prim());
}
return 0;
}
Pro F
题意:拆掉一些可能是环的图中的最小的一些边
具体思路:很多种写法啦~ 一是用所有权和减去最大生成森林~ - - 二是把那些不在生成树的边加起来
参考code:
第一种写法
//
// AOJ 2224 Save your cat
//
// Created by TaoSama on 2015-03-21
// Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
#define CLR(x,y) memset(x, y, sizeof(x))
using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
int n, m, par[10005], rank[10005];
struct Edge {
int u, v;
double cost;
bool operator < (const Edge& rhs) const {
return cost < rhs.cost;
}
};
struct Point {
int x, y;
} a[10005];
vector<Edge> G;
void init(int n) {
for(int i = 1; i <= n; ++i) {
par[i] = i;
rank[i] = 0;
}
}
int find(int x) {
if(par[x] == x) return x;
return par[x] = find(par[x]);
}
void unite(int x, int y) {
x = find(x); y = find(y);
if(x == y) return;
if(rank[x] < rank[y]) {
par[x] = y;
} else {
par[y] = x;
if(rank[x] == rank[y]) ++rank[x];
}
}
bool same(int x, int y) {
return find(x) == find(y);
}
double kruskal() {
double ret = 0;
sort(G.begin(), G.end());
init(n);
for(int i = 0; i < G.size(); ++i) {
Edge &e = G[i];
if(!same(e.u, e.v)) {
ret += e.cost;
unite(e.u, e.v);
}
}
return ret;
}
/* 我就草了!!! 再也不用prim了
double prim() {
double ret = 0;
priority_queue<Sta, vector<Sta>, greater<Sta> > pq;
memset(used, false, sizeof used);
minCost[1] = 0; pq.push(Sta(0, 1));
while(!pq.empty()) {
Sta p = pq.top(); pq.pop();
int u = p.second;
if(used[u] || p.first > minCost[u]) continue;
used[u] = true; ret += minCost[u];
for(int i = 0; i < G[u].size(); ++i) {
Edge &e = G[u][i];
if(minCost[e.to] > e.cost) {
minCost[e.to] = e.cost;
pq.push(Sta(minCost[e.to], e.to));
}
}
}
return ret;
} */
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(scanf("%d%d", &n, &m) == 2) {
double sum = 0; G.clear();
for(int i = 1; i <= n; ++i)
scanf("%d%d", &a[i].x, &a[i].y);
for(int i = 1; i <= m; ++i) {
int x, y; scanf("%d%d", &x, &y);
double cost = sqrt((a[x].x - a[y].x) * (a[x].x - a[y].x) +
(a[x].y - a[y].y) * (a[x].y - a[y].y));
sum += cost;
G.push_back((Edge) {x, y, -cost});
}
double ans = sum + kruskal();
printf("%.3f\n", ans);
}
return 0;
}
Pro G
题意:求来回最短路
具体思路:经典的最短路题目 由于所有的路都是单向的 所以来回的路必然不同(题目在迷惑人Orz)
不管是去还是回 一个终点唯一 一个起点唯一 求两次最短路就好了
对于出发 等于从终点出发到其他点的最短路 这时将边取反!
对于回去 等于从终点出发到其他点的最短路 这时边不变!
参考code:
//
// POJ 3268 Silver Cow Party
//
// Created by TaoSama on 2015-03-20
// Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
#define CLR(x,y) memset(x, y, sizeof(x))
using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
int n, m, s, dp[1005], dp2[1005];
struct Edge {
int to, cost;
};
vector<Edge> G[N], Gr[N];
typedef pair<int, int> Sta;
void dijkstra(vector<Edge> *G, int *dp) {
priority_queue<Sta, vector<Sta>, greater<Sta> > pq;
pq.push(Sta(0, s)); dp[s] = 0;
while(!pq.empty()) {
Sta p = pq.top(); pq.pop();
int u = p.second, d = p.first;
if(d > dp[u]) continue;
for(int i = 0; i < G[u].size(); ++i) {
Edge &e = G[u][i];
if(dp[e.to] > d + e.cost) {
dp[e.to] = d + e.cost;
//printf("dp[%d]: %d\n", e.to, dp[e.to]);
pq.push(Sta(dp[e.to], e.to));
}
}
}
}
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
scanf("%d%d%d", &n, &m, &s);
for(int i = 1; i <= m; ++i) {
int x, y, v; scanf("%d%d%d", &x, &y, &v);
G[x].push_back((Edge) {y, v});
Gr[y].push_back((Edge) {x, v});
}
memset(dp, 0x3f,sizeof dp);
memset(dp2, 0x3f,sizeof dp2);
dijkstra(G, dp); dijkstra(Gr, dp2);
int ans = -INF;
for(int i = 1; i <= n; ++i)
ans = max(ans, dp[i] + dp2[i]);
printf("%d\n", ans);
return 0;
}
Pro H
题意:
有N个房间,每到达一个房间会获得或者减少体力值,
初始位于1号房间,拥有100点体力值,房间可以重复通过来获取体力值。到达某个房间的体力值必须大于0才能获取当前房间的体力值
具体思路:
有正圈且此点连通必然可以到达终点 如果没有正环但是到达终点 体力大于0 也可以
这里把边权取反 求负圈 100个点并用Floyd判断连通性
参考code:
//
//
//
// Created by TaoSama on 2015-03-29
// Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
int n, dp[105];
bool con[105][105];
struct Edge {
int u, v, cost;
};
vector<Edge> G;
bool BellmanFord() {
memset(dp, 0x3f, sizeof dp); dp[1] = -100;
for(int i = 1; i <= n; ++i) {
for(int j = 0; j < G.size(); ++j) {
Edge &e = G[j];
if(dp[e.v] > dp[e.u] + e.cost && dp[e.u] + e.cost < 0) {
dp[e.v] = dp[e.u] + e.cost;
if(i == n && con[1][e.v] && con[e.v][n])
return true;
}
}
}
return false;
}
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(cin >> n && n != -1) {
G.clear();
memset(con, false, sizeof con);
for(int i = 1; i <= n; ++i) con[i][i] = true;
for(int i = 1; i <= n; ++i) {
int c, t, x; cin >> c >> t;
while(t--) {
cin >> x;
G.push_back((Edge) {i, x, -c});
con[i][x] = true;
}
}
for(int k = 1; k <= n; ++k)
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
con[i][j] |= con[i][k] && con[k][j];
if(BellmanFord() || dp[n] < 0) cout << "winnable" << endl;
else cout << "hopeless" << endl;
}
return 0;
}
Pro I
题意:
快递到了:你是某个岛国(ACM-ICPC Japan)上的一个苦逼程序员,你有一个当邮递员的好基友利腾桑遇到麻烦了:全岛有一些镇子通过水路和旱路相连,走水路必须要用船,在X处下船了船就停在X处。而且岛上只有一条船,下次想走水路还是得回到X处才行;两个镇子之间可能有两条以上的水路或旱路;邮递员必须按照清单上的镇子顺序送快递(镇子可能重复,并且对于重复的镇子不允许一次性处理,比如ABCB的话B一定要按顺序走两次才行)。
测试数据有多组:
N M
x1 y1 t1 sl1
x2 y2 t2 sl2
…
xM yM tM slM
R
z1 z2 … zR
N (2 ≤ N ≤ 200) 是镇子的数量,M (1 ≤ M ≤ 10000) 是旱路和水路合计的数量。从第2行到第M + 1行是路径的描述,路径连接xi yi两地,路径花费 ti (1 ≤ ti ≤ 1000)时间,sli 为L时表示是旱路,S时表示是水路。可能有两条及以上路径连接两个镇子,并且路径都是双向的。
M + 2行的R是利腾需要去的镇子的数量,M + 3是利腾需要去的镇子的编号。
初始状态利腾和船都在第一个镇子,且肯定有方法达到需要去的镇子。
测试数据为0 0的时候表示终止。
Sample Input
3 3 1 2 5 L 1 2 7 S 2 3 11 S 3 1 2 3 5 5 1 2 15 L 2 3 10 L 4 5 7 L 1 3 30 S 3 4 100 S 5 1 3 5 4 1 0 0
Output for the Sample Input
18 269
具体思路:
Floyd 预处理 水路和陆路的两点最短距离
这题我没想出来 - - 我dp太弱
具体思路请看注释
参考code:
//
// AOJ 2200 Mr. Rito Post Office
//
// Created by TaoSama on 2015-03-20
// Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
#define CLR(x,y) memset(x, y, sizeof(x))
using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
int sea[205][205], land[205][205], dp[1005][205];
int n, m, r, a[1005];
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(cin >> n >> m && (n + m)) {
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
sea[i][j] = land[i][j] = i == j ? 0 : INF;
for(int i = 1; i <= m; ++i) {
int x, y, t; char c;
cin >> x >> y >> t >> c;
if(c == 'S') sea[x][y] = sea[y][x] = min(sea[x][y], t);
else land[x][y] = land[y][x] = min(land[x][y], t);
}
cin >> r;
for(int i = 1; i <= r; ++i) cin >> a[i];
//Floyd
for(int k = 1; k <= n; ++k) {
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j) {
sea[i][j] = min(sea[i][j], sea[i][k] + sea[k][j]);
land[i][j] = min(land[i][j], land[i][k] + land[k][j]);
}
}
}
//dp[i][j]:= 到达i镇子 船停在j镇子的最小时间
memset(dp, 0x3f, sizeof dp);
dp[1][a[1]] = 0;
for(int i = 1; i <= r; ++i) {
for(int j = 1; j <= n; ++j) {
//一定可以走陆路
dp[i][j] = min(dp[i][j], dp[i - 1][j] + land[a[i - 1]][a[i]]);
for(int k = 1; k <= n; ++k) //枚举水路船停的位置
//从i-1镇子回到船在的j 开船到k镇子船丢在那里 陆路从k到i镇子
//三个INF 可能加爆。。。。。
dp[i][k] = min((long long)dp[i][k], (long long)dp[i - 1][j] +
land[a[i - 1]][j] + sea[j][k] + land[k][a[i]]);
}
}
cout << *min_element(dp[r], dp[r] + n + 1) << endl;
}
return 0;
}
Pro J
题意:
买东西 你有某些种类硬币 每个有有限个 售货员有无限个 你给钱 售货员找钱
求 过程中最少的交换硬币的总和
具体思路:
多重背包 + 完全背包
参考code:
//
//
//
// Created by TaoSama on 2015-03-30
// Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
int n, t, dpp[25000], dpn[15000], c[105], v[105];
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
cin >> n >> t;
for(int i = 1; i <= n; ++i) cin >> v[i];
for(int i = 1; i <= n; ++i) cin >> c[i];
memset(dpp, 0x3f, sizeof dpp);
dpp[0] = 0;
for(int i = 1; i <= n; ++i) {
int k = 1;
while(c[i] > 0) {
int t = min(c[i], k);
for(int j = t + 120 * 120; j >= t * v[i]; --j)
dpp[j] = min(dpp[j], dpp[j - t * v[i]] + t);
c[i] -= k; k <<= 1;
}
}
/*for(int i = 0; i <= t + 120 * 120; ++i)
if(dpp[i] != INF) printf("dpp[%d]: %d\n", i, dpp[i]);*/
memset(dpn, 0x3f, sizeof dpn);
dpn[0] = 0;
for(int i = 1; i <= n; ++i)
for(int j = v[i]; j <= 120 * 120; ++j)
dpn[j] = min(dpn[j], dpn[j - v[i]] + 1);
/*for(int i = 0; i <= 120 * 120; ++i)
if(dpn[i] != INF) printf("dpn[%d]: %d\n", i, dpn[i]);*/
int ans = INF;
for(int i = 0; i <= 120 * 120; ++i)
ans = min(ans, dpp[t + i] + dpn[i]);
if(ans == INF) ans = -1;
cout << ans << endl;
return 0;
}