洛谷-【图论2-2】最短路4

P1993 小 K 的农场

提交答案加入题单复制题目

题目描述

小 K 在 MC 里面建立很多很多的农场,总共 n 个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共 m 个),以下列三种形式描述:

  • 农场 a 比农场 b 至少多种植了 c 个单位的作物;
  • 农场 a 比农场 b 至多多种植了 c 个单位的作物;
  • 农场 a 与农场 b 种植的作物数一样多。

但是,由于小 K 的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。

输入格式

第一行包括两个整数 n 和 m,分别表示农场数目和小 K 记忆中的信息数目。

接下来 m 行:

  • 如果每行的第一个数是 1,接下来有三个整数 a,b,c,表示农场 a 比农场 b 至少多种植了 c 个单位的作物;
  • 如果每行的第一个数是 2,接下来有三个整数 a,b,c,表示农场 a 比农场 b 至多多种植了 c 个单位的作物;
  • 如果每行的第一个数是 3,接下来有两个整数 a,b,表示农场 a 种植的的数量和 b 一样多。

输出格式

如果存在某种情况与小 K 的记忆吻合,输出 Yes,否则输出 No

输入输出样例

输入 #1复制

复制代码
3 3
3 1 2
1 1 3 1
2 2 3 2

输出 #1复制

复制代码
Yes

说明/提示

对于 100% 的数据,保证 1≤n,m,a,b,c≤5×103。

实现代码:

cpp 复制代码
#include <stdio.h>
#include <bits/stdc++.h>

using namespace std;

int n, m, cnt, elast[5005], dis[5005], num[5005];
bool vis[5005];

struct edge {
	int to, len, next;
} e[15005];

queue<int> q;

void add (int u, int v, int w) {
	e[++cnt].to = v;
	e[cnt].len = w;
	e[cnt].next = elast[u];
	elast[u] = cnt;
}

bool spfa (int x) {
	dis[x] = 0;
	q.push(x);
	vis[x] = true;
	num[x]++;
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = false;
		for (int i = elast[u]; i != 0; i = e[i].next)
			if (dis[e[i].to] > dis[u] + e[i].len) {
				dis[e[i].to] = dis[u] + e[i].len;
				if (!vis[e[i].to]) {
					q.push(e[i].to);
					vis[e[i].to] = true;
					num[e[i].to]++;
					if (num[e[i].to] == n + 1)
						return false;
				}
			}
	}
	return true;
}

int main () {
	scanf("%d %d", &n, &m);
	memset(dis, 0x3f3f3f3f, sizeof(dis));
	for (int i = 1; i <= m; i++) {
		int opt;
		scanf("%d", &opt);
		switch (opt) {
			case 1: {
				int a, b, c;
				scanf("%d %d %d", &a, &b, &c);
				add(a, b, -c);
				break;
			}
			case 2: {
				int a, b, c;
				scanf("%d %d %d", &a, &b, &c);
				add(b, a, c);
				break;
			}
			case 3: {
				int a, b;
				scanf("%d %d", &a, &b);
				add(a, b, 0);
				add(b, a, 0);
				break;
			}
		}
	}
	for (int i = 1; i <= n; i++)
		add(n + 1, i, 0);
	bool flag = spfa(n + 1);
	if (!flag) {
		printf("No");
		return 0;
	}
	printf("Yes");
	return 0;
}

P3275 [SCOI2011] 糖果

题目描述

幼儿园里有 N 个小朋友,lxhgww 老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,lxhgww 需要满足小朋友们的 K 个要求。幼儿园的糖果总是有限的,lxhgww 想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。

输入格式

输入的第一行是两个整数 N,K。接下来 K 行,每行 3 个数字 X,A,B,表示小朋友们的要求。

  • 如果 X=1, 表示第 A 个小朋友分到的糖果必须和第 B 个小朋友分到的糖果一样多;
  • 如果 X=2, 表示第 A 个小朋友分到的糖果必须少于第 B 个小朋友分到的糖果;
  • 如果 X=3, 表示第 A 个小朋友分到的糖果必须不少于第 B 个小朋友分到的糖果;
  • 如果 X=4, 表示第 A 个小朋友分到的糖果必须多于第 B 个小朋友分到的糖果;
  • 如果 X=5, 表示第 A 个小朋友分到的糖果必须不多于第 B 个小朋友分到的糖果;

输出格式

输出一行一个整数,表示 lxhgww 老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,则输出 −1。

输入输出样例

输入 #1复制

复制代码
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1

输出 #1复制

复制代码
11

说明/提示

对于 30% 的数据,N≤100;

对于 100% 的数据,1≤N,K≤105,1≤X≤5,1≤A,B≤N。


upd 2022.7.6:新添加 21 组 Hack 数据

实现代码:

cpp 复制代码
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=100010;
struct relationship{
    int x,a,b;
}r[maxn];
struct edge{
    int to,next;
    bool same;
}e[maxn<<1],e2[maxn];
int n,m,num,num2,cnt,head[maxn],head2[maxn],ltk[maxn],dfn[maxn],low[maxn];
int sta[maxn],top,size[maxn],que[maxn],du[maxn],h,tail,candy[maxn];
bool vis[maxn];
long long ans=0;
void add(int u,int v,bool k){
    e[++num].to=v;e[num].same=k;e[num].next=head[u];head[u]=num;
}
void add2(int u,int v,bool k){
    du[v]++;
    e2[++num2].to=v;e2[num2].same=k;e2[num2].next=head2[u];head2[u]=num2;
}
void dfs(int x){
    dfn[x]=low[x]=++cnt;sta[++top]=x;
    for(int i=head[x];i;i=e[i].next){
        if(!dfn[e[i].to]){
            dfs(e[i].to);
            low[x]=min(low[x],low[e[i].to]);
        }
        else if(!ltk[e[i].to]){
            low[x]=min(low[x],dfn[e[i].to]);
        }
    }
    if(dfn[x]==low[x]){
        ltk[0]++;
        while(top){
            ltk[sta[top]]=ltk[0];
            size[ltk[0]]++;
            if(sta[top--]==x)break;
        }
    }
}
void Rebuild(){
    for(int i=1;i<=n;i++){
        for(int j=head[i];j;j=e[j].next){
            if(ltk[i]!=ltk[e[j].to]){
                add2(ltk[i],ltk[e[j].to],true);
            }
        }
    }
    for(int i=1;i<=m;i++){
        if(r[i].x==2){
            if(ltk[r[i].a]==ltk[r[i].b]){
                printf("-1\n");
                exit(0);
            }
            else{
                add2(ltk[r[i].a],ltk[r[i].b],false);
            }
        }
        else if(r[i].x==4){
            if(ltk[r[i].a]==ltk[r[i].b]){
                printf("-1\n");
                exit(0);
            }
            else{
                add2(ltk[r[i].b],ltk[r[i].a],false);
            }
        }
    }
}
void Topsort(){
    for(int i=1;i<=ltk[0];i++){
        if(!du[i]){
            que[++tail]=i;
            vis[i]=true;
            candy[i]=1;      
        }
    }
    while(h<tail){
        int x=que[++h];
        for(int i=head2[x];i;i=e2[i].next){
            if(!--du[e2[i].to]){
                que[++tail]=e2[i].to;
                vis[e2[i].to]=true;
            }
            if(!e2[i].same){
                candy[e2[i].to]=max(candy[e2[i].to],candy[x]+1);
            }
            else{
                candy[e2[i].to]=max(candy[e2[i].to],candy[x]);
            }
        }
    }
    for(int i=1;i<=ltk[0];i++){
        if(!vis[i]){
            printf("-1\n");
            exit(0);
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&r[i].x,&r[i].a,&r[i].b);
        if(r[i].x==1){
            add(r[i].a,r[i].b,true);
            add(r[i].b,r[i].a,true);
        }
        else if(r[i].x==3){
            add(r[i].b,r[i].a,true);
        }
        else if(r[i].x==5){
            add(r[i].a,r[i].b,true);
        }
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]){
            dfs(i);
        }
    }
    Rebuild();Topsort();
    for(int i=1;i<=ltk[0];i++){
        ans+=(candy[i]*size[i]);
    }
    printf("%lld\n",ans);
    return 0;
}

P6464 [传智杯 #2 决赛] 传送门

题目描述

传智专修学院里有 n 栋教学楼,有 m 条双向通行道路连接这些教学楼,不存在重边和自环。每条道路都有一定的长度,而且所有教学楼之间都可以直接或者间接的通过道路到达。我们可以很容易的求出这些教学楼之间的最短路。

为了使交通更为顺畅,校方决定在两个教学楼里增设一对传送门。传送门可以将这对教学楼的距离直接缩短为 0。利用传送门,某些教学楼之间的最短路的距离就变短了。

由于预算有限,学校里只能安装一对传送门。但是校长希望尽可能方便学生,使任意两点之间的最短路长度的总和最小。当然啦,从 x 教学楼到 y 教学楼的长度和从 y 教学楼到 x 教学楼的长度只需要统计一次就可以了。

输入格式

输入第 1 行两个正整数 n,m(n≤100,m≤21​n(n−1)),代表教学楼和道路数量。

接下来 m 行,每行三个正整数 xi​,yi​,wi​(0<wi​≤104),表示在教学楼 xi​ 和 yi​ 之间,有一条长度为 wi​ 的道路。

输出格式

输出一行,在最优方案下的任意点对的最短道路之和。

输入输出样例

输入 #1复制

复制代码
4 5
1 2 3
1 3 6
2 3 4
2 4 7
3 4 2

输出 #1复制

复制代码
14

说明/提示

样例如图。当在 1 和 4 号教学楼架设一对传送门时,1 → 2 的最短路是 3,1 → 3 的最短路是 0+2,1 → 4 的最短路是 0,2 → 3 的最短路是 4,2 → 4 的最短路是 3+0,3 → 4 的最短路是 2,最短路之和是 14,是最佳方案。

实现代码:

cpp 复制代码
#include<bits/stdc++.h>
#define FOR(i,j,k)  for(int i=(j);i<=(k);i++)
using namespace std;
int n,m;
int f[101][101];
int F[101][101];
inline void back()
{
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			F[i][j]=f[i][j];
}
int main()
{
	scanf("%d%d",&n,&m);
	memset(f,-1,sizeof(f));
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		if(f[u][v]==-1||f[u][v]>w)	f[u][v]=f[v][u]=w;
	}
	for(int k=1;k<=n;k++)	
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				if(f[i][k]!=-1&&f[k][j]!=-1)
					if(f[i][j]==-1||f[i][j]>f[k][j]+f[i][k])
						f[i][j]=f[i][k]+f[k][j];
	int ans=2e9;
	for(int i=1;i<=n;i++)	
		for(int j=1;j<=n;j++)
		{
			back();
			F[i][j]=F[j][i]=0;
			for(int x=1;x<=n;x++)	
				for(int y=1;y<=n;y++)	
					if(F[x][y]==-1||F[x][y]>F[x][i]+F[i][y])
						F[x][y]=F[x][i]+F[i][y];
			for(int x=1;x<=n;x++)
				for(int y=1;y<=n;y++)	
					if(F[x][y]==-1||F[x][y]>F[x][j]+F[j][y])
						F[x][y]=F[x][j]+F[j][y];
			int res=0;
			for(int x=1;x<=n;x++)	
				for(int y=1;y<x;y++)
					res+=F[x][y];
			ans=min(res,ans);
		}
	printf("%d\n",ans);
	return 0;
}

P3403 跳楼机

题目背景

DJL 为了避免成为一只咸鱼,来找 srwudi 学习压代码的技巧。

题目描述

Srwudi 的家是一幢 h 层的摩天大楼。由于前来学习的蒟蒻越来越多,srwudi 改造了一个跳楼机,使得访客可以更方便的上楼。

经过改造,srwudi 的跳楼机可以采用以下四种方式移动:

  1. 向上移动 x 层;
  2. 向上移动 y 层;
  3. 向上移动 z 层;
  4. 回到第一层。

一个月黑风高的大中午,DJL 来到了 srwudi 的家,现在他在 srwudi 家的第一层,碰巧跳楼机也在第一层。DJL 想知道,他可以乘坐跳楼机前往的楼层数。

输入格式

第一行一个整数 h,表示摩天大楼的层数。

第二行三个正整数,分别表示题目中的 x,y,z。

输出格式

一行一个整数,表示 DJL 可以到达的楼层数。

输入输出样例

输入 #1复制

复制代码
15
4 7 9

输出 #1复制

复制代码
9

输入 #2复制

复制代码
33333333333
99005 99002 100000

输出 #2复制

复制代码
33302114671

说明/提示

【样例 1 解释】

可以到达的楼层有:1,5,8,9,10,12,13,14,15。

【数据规模与约定】

对于 100% 的数据,1≤h≤263−1,1≤x,y,z≤105。

实现代码:

cpp 复制代码
#include <iostream>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <climits>
#include <algorithm>
#include <vector>

typedef long long ll;

ll fr() {
    ll x = 0, f = 1;
    char c = getchar();
    while (!isdigit(c)) {
        if (c == '-') f = -1;
        c = getchar();
    }
    while (isdigit(c)) {
        x = (x << 3) + (x << 1) + (c ^ 48);
        c = getchar();
    }
    return x * f;
}

const int maxn = 1e5+100;

ll d[maxn];
bool vis[maxn];
ll ans;

int gcd(int a,int b) {
    return b?gcd(b,a%b):a;
}

void upd(int step,int M) {
    int D=gcd(step,M);
    int len=M/D;
    
    for (int st = 0; st < D; st++) {
        if (vis[st]) continue;
        std::vector<int> v;
        int u=st;
        for (int i = 0; i < len; i++) {
            v.push_back(u);
            vis[u]=true;
            u=(u+step)%M;
        }
        for (int r = 0; r < 2; r++) {
            for (int i = 0; i < len; i++) {
                int las=v[i];
                int now=v[(i+1)%len];
                if (d[las]!=LLONG_MAX) d[now]=std::min(d[now],d[las]+step);
            }
        }
    }
}

int main() {
    ll h = fr();
    int x[3];
    for (int i = 0; i < 3; i++) x[i]=fr();
    std::sort(x,x+3);
    int M=x[0];
    
    for (int i = 0; i < M; i++) d[i]=LLONG_MAX;
    d[0]=0;
    
    upd(x[1],M);
    memset(vis,0,sizeof(vis));
    upd(x[2],M);
    
    ll H=h-1;
    ans=0;
    for (int i = 0; i < M; i++) {
        if (d[i]<=H&&d[i]!=LLONG_MAX) {
            ans+=(H-d[i])/M+1;
        }
    }
    printf("%lld\n",ans);
    return 0;
}
相关推荐
我爱cope2 小时前
【力扣hot100:239. 滑动窗口最大值】
算法·leetcode·职场和发展
輕華2 小时前
YOLOv10轮毂缺陷检测(下)——模型推理与PyQt5可视化应用
开发语言·qt·yolo
kyle~2 小时前
机器人感知---工业相机硬触发、时间戳同步( PTP)与 ROS2 驱动时间戳设计
linux·c++·机器人·ros2
XINVRY-FPGA2 小时前
XC7Z010-2CLG400I Xilinx Zynq-7000 FPGA
arm开发·嵌入式硬件·算法·fpga开发·硬件工程·dsp开发·fpga
努力攀登的小k2 小时前
《Java基础,Java多态入门到进阶:重写、重载、转型的逻辑与实战避坑》
java·开发语言
承渊政道2 小时前
【贪心算法】(经典实战应用解析(四):分发饼干、最优除法、跳跃游戏、跳跃游戏Ⅱ、加油站)
数据结构·c++·算法·leetcode·贪心算法·动态规划·哈希算法
烤麻辣烫2 小时前
计算机思维--经典互联网应用
开发语言·学习·搜索引擎·数据库开发
m0_629494732 小时前
LeetCode 热题 100-----24.回文链表
数据结构·算法·leetcode·链表
爱怪笑的小杰杰2 小时前
Leaflet 实现轨迹拐角自动圆弧化:基于球面几何的高精度平滑算法
前端·javascript·算法·无人机