其他
挑战程序设计竞赛
1.POJ 3259 Wormholes
- 这道题后来我又想了一个方法,只能说这个方法不是完全正确,还要因题而定。为什么这么说呢?我用的是Bellman-Ford找负圈,可以这么说,若存在负圈,且这个负圈和起点是连通的,则此法是正确的。可见,此题这个方法是可以的。这个方法很快,用邻接表也是很快很方便的,只用160ms。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int N, E, M, W, T;
const int maxn = 5205;
struct edge
{
int from, to, cost;
};
edge es[5205];
int d[maxn];
bool find_negative_loop()
{
memset(d, 0, sizeof(d));
for(int i = 0; i < N; i++){
for(int j = 0; j < E; j++){
edge e = es[j];
if(d[e.to] > d[e.from] + e.cost){
d[e.to] = d[e.from] + e.cost;
if(i == N - 1) return true;
}
}
}
return false;
}
int main()
{
scanf("%d", &T);
while(T--){
scanf("%d%d%d", &N, &M, &W);
E = 2 * M + W;
int s, t, co;
for(int i = 0; i < M; i++){
scanf("%d%d%d", &s, &t, &co);
es[i] = {s, t, co};
es[i + M] = {t, s, co};
}
for(int i = 2 * M; i < W + 2 * M; i++){
scanf("%d%d%d", &s, &t, &co);
es[i] = {s, t, -co};
}
if(find_negative_loop()) printf("YES\n");
else printf("NO\n");
}
return 0;
}
- 当然还有法二,用Floyd-Warshall算法。但是比较慢,1600ms。有一些注意事项:这个题,首先要提醒,可能出现重边,要选择最小的那条边。其次,我发现把min换成if判断语句,时间至少快了400MS。 欸,输出是"YES\n"还是"Yes\n"啊,看清楚行不!
#include<cstdio>
using namespace std;
int F, N, M, W;
const int INF = 1e8;
int cost[505][505];
void solve()
{
for(int k = 1; k <= N; k++){
for(int i = 1; i <= N; i++){
for(int j = 1; j <= N; j++){
if(cost[i][j] > cost[i][k] + cost[k][j]){
cost[i][j] = cost[i][k] + cost[k][j];
}
}
if(cost[i][i] < 0){
printf("YES\n");
return;
}
}
}
printf("NO\n");
}
int main()
{
scanf("%d", &F);
int s, e, t;
while(F--){
scanf("%d%d%d", &N, &M, &W);
for(int i = 1; i <= N; i++){
for(int j = 1; j <= N; j++){
cost[i][j] = INF;
}
cost[i][i] = 0;
}
//把这个cost[i][i] = 0放前面,万一只有一个负环怎么办?
for(int i = 1; i <= M; i++){
scanf("%d%d%d", &s, &e, &t);
if(cost[s][e] > t){
cost[s][e] = cost[e][s] = t;
}
}
for(int i = 1; i <= W; i++){
scanf("%d%d%d", &s, &e, &t);
cost[s][e] = -t;
}
solve();
}
return 0;
}
kuangbin系列
1.POJ 2253 Frogger
- 二分法加并查集好像还挺常用的,因此把这道题记录下来。
- 感觉二分法加并查集经常用于最大化最小值,而且不用将每个点都连起来,也不关心总和是否最小。
//这道题并不难,但并非是寻找最短路,而是最小化最大值,因此看到这里想到了二分法加并查集。
//最后还是想说一句,仔细读题,别总是因为输出格式不对而犯错误。
//复杂度应该是n^2*log(n)。
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 205;
int par[maxn], ranking[maxn];
void init(int n)
{
for(int i = 0; i < n; i++){
par[i] = i;
ranking[i] = 0;
}
}
int find(int x)
{
if(par[x] == x){
return x;
}
else{
return par[x] = find(par[x]);
}
}
void unite(int x, int y)
{
x = find(x);
y = find(y);
if(x == y){
return;
}
if(ranking[x] < ranking[y]){
par[x] = y;
}
else{
par[y] = x;
if(ranking[x] == ranking[y]){
ranking[x]++;
}
}
}
bool same(int x, int y)
{
return find(x) == find(y);
}
int N, kase;
int x[maxn], y[maxn];
double dis(int m, int n)
{
double dx = (x[m] - x[n]) * (x[m] - x[n]), dy = (y[m] - y[n]) * (y[m] - y[n]);
return sqrt(dx + dy);
}
bool C(double x)
{
init(N);
for(int i = 0; i < N; i++){
for(int j = i + 1; j < N; j++){
if(!same(i, j) && dis(i, j) <= x){
unite(i, j);
}
}
}
return same(0, 1);
}
void solve()
{
double lb = 0, ub = 1e9;
for(int i = 0; i < 100; i++){
double mid = (lb + ub) / 2;
if(C(mid)){
ub = mid;
}
else{
lb = mid;
}
}
printf("Scenario #%d\nFrog Distance = %.3f\n\n", ++kase, lb);
}
int main()
{
while(scanf("%d", &N) && N){
for(int i = 0; i < N; i++){
scanf("%d%d", &x[i], &y[i]);
}
solve();
}
return 0;
}
POJ 1502 MPI Maelstrom
- (1)题意很长很扯淡,其实题的意思很简单。这道题输入的处理还是有一些难的,但是有很巧妙的方法。需要指出的是,atoi()这个函数很有意思,可以把字符串边成整数,但是也是有大小限制的,返回值是int。在中就有定义。不得不说,里面真是啥都有,写代码时顺便把他带上也挺好的。
- (2)用dijstra算法一定要注意P那个地方,first和second别写反了。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 105, INF = 1e8;
int N;
struct edge
{
int to, cost;
};
vector<edge> G[maxn];
char str[15];
int d[maxn];
typedef pair<int, int> P;
void solve()
{
fill(d, d + N, INF);
d[0] = 0;
priority_queue<P, vector<P>, greater<P> > que;
que.push(P(0, 0));
while(!que.empty()){
P p = que.top();
que.pop();
int v = p.second;
if(d[v] < p.first){
continue;
}
int sz = G[v].size();
for(int i = 0; i < sz; i++){
edge e = G[v][i];
if(d[e.to] > d[v] + e.cost){
d[e.to] = d[v] + e.cost;
que.push(P(d[e.to], e.to));
}
}
}
int res = *max_element(d, d + N);
printf("%d\n", res);
}
int main()
{
scanf("%d", &N);
for(int i = 1; i < N; i++){
for(int j = 0; j < i; j++){
scanf("%s", str);
if(str[0] == 'x'){
continue;
}
int co = atoi(str);
G[i].push_back({j, co});
G[j].push_back({i, co});
}
}
solve();
return 0;
}
POJ 1511 Invitation Cards
- (1)这道题数据大的扯淡,用邻接表,即挑战程序设计竞赛上面教的方法直接超时。我猜测要用Spfa算法。
POJ 3159 Candies
- (1)又是尼玛超时呀,但是这家活用差分约束就没事呀,听不懂呀,当当当挡,传送门POJ3159 Candies 差分约束。
cf
1.K. Birdwatching
-
如果满足题意的点得话,那么只能染成一种颜色才对。但是染了两种颜色,说明是从“其他点”(这里指与题目给的起点的相邻的点)走过来的。因为建了反图,因此从这个点可以走到那个“其他点”。即从这个点不止一条路径可以到题目给的那个起点。
-
其实一个点只需要染出两个颜色就可以断言不满足条件。因此只开两个color数组就行,简化步骤。
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
const int maxn = 100010;
int h[maxn], e[maxn], ne[maxn], idx, N, M, K;
int color1[maxn], color2[maxn];
vector<int> ans;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u, int c) {
//染色是不允许通过树根的!勿忘!
if (u == K) return;
if (color1[u] == -1) color1[u] = c;
else if (color2[u] == -1 && color1[u] != c) color2[u] = c;
else return;
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
dfs(v, c);
}
}
int main() {
memset(h, -1, sizeof h);
memset(color1, -1, sizeof color1);
memset(color2, -1, sizeof color2);
scanf("%d%d%d", &N, &M, &K);
for (int i = 0; i < M; i++) {
int a, b;
scanf("%d%d", &a, &b);
add(b, a);
}
for (int i = h[K]; i != -1; i = ne[i]) {
dfs(e[i], e[i]);
}
for (int i = h[K]; i != -1; i = ne[i]) {
if (color1[e[i]] != -1 && color2[e[i]] == -1) ans.push_back(e[i]);
}
sort(ans.begin(), ans.end());
printf("%d\n", ans.size());
for (auto p : ans) {
printf("%d\n", p);
}
return 0;
}
2.C Coolest Ski Route
- 这道题是一个有向无环图,求路径最大长度。可以用动态规划+拓扑排序来做!这其实是一种新思路。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1010, maxm = 5010;
int f[maxn], e[maxm], w[maxm], h[maxm], ne[maxm], idx, N, M;
int dp(int u) {
int& v = f[u];
if (v != -1) return v;
v = 0;
for (int i = h[u]; i != -1; i = ne[i]) {
v = max(v, dp(e[i])+ w[i]) ;
}
return v;
}
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int main() {
memset(h, -1, sizeof h);
memset(f, -1, sizeof f);
scanf("%d%d", &N, &M);
for (int i = 0; i < M; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
int res = 0;
for (int i = 1; i <= N; i++) {
res = max(res, dp(i));
}
printf("%d\n", res);
return 0;
}
3.I Iron and Coal
- 这道题啊,是找到一个节点,使得到起点,煤矿和铁矿的距离之和最短。
- 第一步很好找,就是从起点开始搜单源最短距离。
- 第二步,建立虚拟原点和反图,进行两次宽度优先搜索。然后把三个距离相加找最小值。
- 虚拟节点:建一个节点,到多个源点的距离为0.
- 注意一定要判断三个dist是否都为INF。不要直接相加,不然会爆int。
- 这道题下次再用虚拟节点的时候用BFS!别用dijstra
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 100010, maxm = 1000010, INF = 0x3f3f3f3f;
int dist[maxn], dist1[maxn], dist2[maxn];
int h1[maxn], e1[maxm], ne1[maxm], idx1;
int h2[maxn], e2[maxm], ne2[maxm], idx2;
int iron[maxn], coal[maxn];
int N, M, K;
bool vis[maxn], vis1[maxn], vis2[maxn];
typedef pair<int, int> P;
void add(int a, int b) {
e1[idx1] = b, ne1[idx1] = h1[a], h1[a] = idx1++;
e2[idx2] = a, ne2[idx2] = h2[b], h2[b] = idx2++;
}
void bfs1() {
for (int i = 1; i <= N; i++) dist[i] = INF;
priority_queue<P, vector<P>, greater<P> > que;
dist[1] = 0;
que.push({ 0, 1 });
while (que.size()) {
P p = que.top(); que.pop();
int u = p.second; int d = p.first;
if (vis[u]) continue;
vis[u] = 1;
for (int i = h1[u]; i != -1; i = ne1[i]) {
int v = e1[i];
if (dist[v] > d + 1) {
dist[v] = d + 1;
que.push({ dist[v], v });
}
}
}
}
void bfs2(int d[], int sz, int matter[], bool vis[]) {
for (int i = 1; i <= N; i++) d[i] = INF;
priority_queue<P, vector<P>, greater<P> > que;
for (int i = 0; i < sz; i++) {
d[matter[i]] = 0;
que.push({ 0, matter[i] });
}
while (que.size()) {
P p = que.top(); que.pop();
int u = p.second; int dist = p.first;
if (vis[u]) continue;
vis[u] = true;
for (int i = h2[u]; i != -1; i = ne2[i]) {
int v = e2[i];
if (d[v] > dist + 1) {
d[v] = dist + 1;
que.push({ d[v], v });
}
}
}
}
int main() {
memset(h1, -1, sizeof h1);
memset(h2, -1, sizeof h2);
scanf("%d%d%d", &N, &M, &K);
for (int i = 0; i < M; i++) {
scanf("%d", &iron[i]);
}
for (int i = 0; i < K; i++) {
scanf("%d", &coal[i]);
}
for (int i = 1; i <= N; i++) {
int a, b;
scanf("%d", &a);
for (int j = 0; j < a; j++) {
scanf("%d", &b);
add(i, b);
}
}
bfs1();
bfs2(dist1, M, iron, vis1);
bfs2(dist2, K, coal, vis2);
int ans = INF;
for (int i = 1; i <= N; i++) {
if (dist[i] != INF && dist1[i] != INF && dist2[i] != INF) {
ans = min(ans, dist[i] + dist1[i] + dist2[i]);
}
}
if (ans == INF) printf("impossible\n");
else printf("%d\n", ans);
return 0;
}
4.H Hyacinth
- 这道题,用染色很简单。因为dfs可以保证向一个方向往前跑。因此,每次都把一个结点的第一个颜色染成上一个节点的第二个颜色。第二个颜色始终染成cnt++。
- 因为这道题最开始dfs的节点不会有前驱节点,需要直接染上两个颜色(1,2)。但是还要注意:如果这个节点出度为1,就要把下一个节点两个颜色都要染成这两个颜色(1和2)。否则,染成1和cnt++。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 10010, maxm = 20010;
int h[maxn], ne[maxm], e[maxm], idx, degree[maxn];
int N, cnt = 3;
int color1[maxn], color2[maxn];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u) {
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if (color1[v] && color2[v]) continue;
else{
color1[v] = color2[u];
color2[v] = cnt++;
dfs(v);
}
}
}
int main() {
memset(h, -1, sizeof h);
scanf("%d", &N);
for (int i = 1; i < N; i++) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
add(b, a);
degree[a]++, degree[b]++;
}
int v = e[h[1]];
color1[1] = color1[v] = 1;
color2[1] = 2;
if (degree[1] == 1) color2[v] = 2;
else color2[v] = cnt++;
dfs(1);
dfs(v);
for (int i = 1; i <= N; i++) {
printf("%d %d\n", color1[i], color2[i]);
}
return 0;
}
5.D Digi Comp II
- 这道题呀,和刘汝佳的那个题很相似,都是一堆小球从1号节点滚下来,每个节点都标注着’L’和’R’。但是这个题不是二叉树,只是一个出度为2的很普通的树。
- 在bfs一开始的地方,把所有入度为0的节点全部推入que。一开始我还在想这个有必要嘛?其实是很有必要的!因为中间有一步是入度为0的时候才会推入que。如果一开始不把入度为0的点推入que,有些节点的入度就降不到0了。
- 看到那个static que que了吧。声明为静态局部变量,可以防止栈溢出。不过要注意得把que清空。
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 500010;
ll balls[maxn];
int g[maxn][2], in[maxn];
bool st[maxn];
int N;
ll M;
void bfs() {
//用静态局部变量,防止栈溢出!
static queue<int> que;
while (que.size()) que.pop();
for (int i = 1; i <= N; i++) {
if (in[i] == 0) que.push(i);
}
while (que.size()) {
int u = que.front(); que.pop();
ll n = balls[u];
bool flag = st[u];
if (n % 2) st[u] = !st[u];
int l = g[u][0], r = g[u][1];
if (l) {
if (!flag) balls[l] += (n + 1) / 2;
else balls[l] += n / 2;
in[l]--;
if (in[l] == 0) que.push(l);
}
if (r) {
if (!flag) balls[r] += n / 2;
else balls[r] += (n + 1) / 2;
in[r]--;
if (in[r] == 0) que.push(r);
}
}
}
int main() {
char op[5];
scanf("%lld%d", &M, &N);
for (int i = 1; i <= N; i++) {
int a, b;
scanf("%s%d%d", op, &a, &b);
g[i][0] = a;
g[i][1] = b;
in[a]++, in[b]++;
if (op[0] == 'L') st[i] = false;
else st[i] = true;
}
balls[1] = M;
bfs();
for (int i = 1; i <= N; i++) {
printf("%c", st[i] ? 'R' : 'L');
}
return 0;
}
6.B. Two Buttons
法一:
*这道题可以用建图的方法去写。建立 1 0 4 10^4 104个节点, 2 ∗ 1 0 4 2 * 10 ^4 2∗104条边即可。然后用Dij算法就可以很快的求出来。
- 因为这道题不可能经过节点数字超过10000的情况,因此10000以上是不用建图的。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 20010, maxm = 40010, INF = 0x3f3f3f3f;
int h[maxn], e[maxm], ne[maxm], idx;
int d[maxn], N, M;
bool vis[maxn];
typedef pair<int, int> P;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int bfs() {
fill(d, d + maxn, INF);
static priority_queue<P, vector<P>, greater<P> > que;
while (que.size()) que.pop();
que.push({0, N});
d[N] = 0;
while (que.size()) {
auto p = que.top(); que.pop();
int u = p.second, dist = p.first;
if (vis[u]) continue;
vis[u] = true;
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if (d[v] > dist + 1) {
d[v] = dist + 1;
que.push({ d[v], v });
}
}
}
return d[M];
}
int main() {
scanf("%d%d", &N, &M);
memset(h, -1, sizeof h);
for (int i = 1; i <= 10000; i++) {
if (i - 1 > 0) add(i, i - 1);
if (i * 2 <= 10000) add(i, i * 2);
}
int ans = bfs();
printf("%d\n", ans);
return 0;
}
法二:
- 由于我们刚才分析了,一定是从下面乘2这样趋近是最快的。我们来反着想,由m到n,也就是m只能加1或者除以2。如果m是奇数,那么必然最后一步是减操作,我们可以确定,因此可以先加回去m变成m+1。如果是偶数,那么必然最后一步是乘以2也可以还原,把m变成m/2
#include<cstdio>
#include<algorithm>
using namespace std;
int main() {
int N, M;
scanf("%d%d", &N, &M);
int cnt = 0;
if (N >= M) printf("%d", N - M);
else {
while (M > N) {
if (M % 2) cnt++, M++;
cnt++;
M /= 2;
}
printf("%d\n", cnt + N - M);
}
return 0;
}
7.B. Two Buttons
- 这道题就是说把N变成M,然后N只能减1或是乘2。这道题我一开始用的是最短路。其实可以简单的用bfs就可以模拟出这个过程。而且复杂度也降低了很多。
- 这道题,最短路路径的坐标最大就是M+1,不可能是更大的坐标
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 20010, INF = 0x3f3f3f3f;
int d[maxn], N, M;
void bfs() {
queue<int> que;
fill(d, d + maxn, INF);
d[N] = 0;
que.push(N);
while (que.size()) {
int u = que.front(); que.pop();
if (2 * u <= 2 * M && d[2 * u] > d[u] + 1) {
d[2 * u] = d[u] + 1;
que.push(2 * u);
}
if (u > 1 && d[u - 1] > d[u] + 1) {
d[u - 1] = d[u] + 1;
que.push(u - 1);
}
}
}
int main() {
scanf("%d%d", &N, &M);
bfs();
printf("%d\n", d[M]);
return 0;
}
8.B. Hierarchy
- 只要除了最高领导者之外,每个人有且仅有一个上司,我们就构建出来了一个树。因此,就可以用最小生成树去写。只有一点需要变动,就是加一个判断,一个人有且仅有有一个上司,否则continue
typedef long long ll;
int q[maxn], N, M, p[maxn];
bool has_parents[maxn], one_sup[maxn];
void init(int N) {
for (int i = 1; i <= N; i++) p[i] = i;
}
int find(int x) {
if (p[x] == x) return x;
return p[x] = find(p[x]);
}
void unite(int x, int y) {
if (find(x) == find(y)) return;
p[find(x)] = find(y);
}
struct P {
int u, v, w;
bool operator <(const P& rhp)const {
return w < rhp.w;
}
}G[maxm];
ll solve() {
init(N);
int cnt = 0;
for (int i = 1; i <= N; i++) {
cnt += !has_parents[i];
}
if (cnt != 1) return -1;
ll res = 0;
sort(G, G + M);
for (int i = 0; i < M; i++) {
int u = G[i].u, v = G[i].v;
if (find(u) == find(v) || one_sup[v]) continue;
unite(u, v);
res += G[i].w;
one_sup[v] = true;
}
return res;
}
9.A. The Two Routes
题意:告诉你n个点的完全图,有m条边是铁路,其余的边是马路,一个人走铁路一个人走马路,问两个人都到达点n的时间。
- cf蒙人题。1个N之间必然直接连接一条路,要么是公路,要么是铁路。即肯定有1种方式会直接到终点。根本就不会相撞。剩下只需要看看剩下的那个人到达N的时间即可。
- 但是有一个需要注意,需要修改Dijkstra朴素算法,其实只需要在里面加一句判断就行:if (g[t][j] != k) continue;
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 405, INF = 1e9;
int g[maxn][maxn], N, M, d[maxn];
bool vis[maxn];
void bfs(int k) {
fill(d, d + maxn, INF);
d[1] = 0;
for (int i = 0; i < N; i++) {
int t = -1;
for (int j = 1; j <= N; j++) {
if (!vis[j] && (t == -1 || d[t] > d[j])) t = j;
}
vis[t] = true;
for (int j = 1; j <= N; j++) {
if (g[t][j] != k) continue;
d[j] = min(d[j], d[t] + 1);
}
}
if (d[N] != INF) printf("%d\n", d[N]);
else printf("-1\n");
}
int main() {
scanf("%d%d", &N, &M);
for (int i = 0; i < M; i++) {
int a, b;
scanf("%d%d", &a, &b);
g[a][b] = g[b][a] = 1;
}
bfs(!g[1][N]);
return 0;
}
10.A. Fair
- 再甩一道虚拟节点的题目。其实,虚拟节点啥也不是,就是辅助bfs理解的。但是,bfs其实和迷宫那个模板是一模一样的。其实网上动不动都说bfs,用的都是迷宫那个模板的改编。
- 注意,这种题都是反向求解,找到每个货物到城镇的最短距离,也就是bfs搜索k次。然后,对于每个城镇,把这K个结果加起来。
- 可以用nth_element这个STL,具体的可以搜一搜(收藏里面有),很方便。
- 注意这种题再相加距离的时候一定要注意INF,因为可能会撑爆int。
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
const int maxn = 100010, maxm = 200010, INF = 1e9;
int h[maxn], e[maxm], ne[maxm], idx;
int d[105][maxn], N, M, K, S;
bool vis[maxn];
vector<int> kinds[105];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void bfs(int k) {
memset(vis, 0, sizeof vis);
fill(d[k], d[k] + N + 1, INF);
queue<int> que;
for (auto p : kinds[k]) {
vis[p] = true;
d[k][p] = 0;
que.push(p);
}
while (que.size()) {
int u = que.front();
que.pop();
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if (vis[v]) continue;
d[k][v] = d[k][u] + 1;
vis[v] = true;
que.push(v);
}
}
}
int main() {
scanf("%d%d%d%d", &N, &M, &K, &S);
for (int i = 1; i <= N; i++) {
int x;
scanf("%d", &x);
kinds[x].push_back(i);
}
memset(h, -1, sizeof h);
for (int i = 0; i < M; i++) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
add(b, a);
}
for (int i = 1; i <= K; i++) {
bfs(i);
}
int total[105];
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= K; j++) {
total[j] = d[j][i];
}
nth_element(total + 1, total + S + 1, total + K + 1);
int res = 0;
for (int j = 1; j <= S; j++) {
res += total[j];
}
printf("%d ", res);
}
return 0;
}
11.903. 昂贵的聘礼
- 其实可以增加一个虚拟源点,让这个源点到每个物品的距离就是直接购买的价钱。这样子的话,起点就是虚拟源点(可以设为0),终点就是1(酋长的女儿)。
- 或者是将酋长当作起点,最后只需要 ans = min(ans, d[i] + cost[i]).
- 但是等级没有转换对。其实只需要枚举区间就行,就是[grade[i] - M, grade[1]] ~ [grade[1], grade[1] + M].
- 多次循环的话,初始化千万别忘!
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
using namespace std;
const int maxn = 110, INF = 1e9;
int g[maxn][maxn], N, M, d[maxn], cost[maxn], grade[maxn];
bool vis[maxn];
int bfs(int l, int r) {
fill(d, d + maxn, INF);
memset(vis, false, sizeof vis);
d[1] = 0;
for (int i = 0; i < N; i++) {
int t = -1;
for (int j = 1; j <= N; j++) {
if (!vis[j] && (t == -1 || d[j] < d[t])) t = j;
}
vis[t] = true;
for (int j = 1; j <= N; j++) {
if (l <= grade[j] && grade[j] <= r) {
d[j] = min(d[j], d[t] + g[t][j]);
}
}
}
int ans = cost[1];
for (int i = 1; i <= N; i++) {
if (d[i] == INF) continue;
ans = min(ans, d[i] + cost[i]);
}
return ans;
}
int main() {
scanf("%d%d", &M, &N);
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) g[i][j] = INF;
}
for (int i = 1; i <= N; i++) {
int P, L, X;
scanf("%d%d%d", &P, &L, &X);
cost[i] = P, grade[i] = L;
for (int j = 0; j < X; j++) {
int T, V;
scanf("%d%d", &T, &V);
g[i][T] = min(g[i][T], V);
}
}
int ans = INF;
for (int i = 0; i <= M ; i++) {
ans = min(ans, bfs(grade[1] - M + i, grade[1] + i));
}
printf("%d\n", ans);
return 0;
}