开心小屋(smile)

开心小屋(smile) ⁡ \operatorname{开心小屋(smile)} (smile)

题目链接: SSL比赛 1498 ⁡ \operatorname{SSL比赛\ 1498} SSL 1498

题目

Kc 来到开心小屋。开心小屋是用来提升心情的。在这个小屋中有 n n n 个房间,一些房间之间有门连通。从房间 i i i 到达房间 j j j ,心情值可以加上 − 10000 < = C i j < = 10000 -10000<=C_{ij}<=10000 10000<=Cij<=10000 ,当然 C i j C_{ij} Cij 可能是负的。现在 kc 失恋了,所以他想要知道他是否可以在这个小屋中无限地增加他的心情值,也就是无限地绕着一个环走?

请帮 kc 求出最小的环需要经过的房间数,来使他的心情无限增加。

输入

第一行给出, 1 < = n < = 300 1<=n<=300 1<=n<=300 1 < = m < = 5000 1<=m<=5000 1<=m<=5000 。分别表示房间数及门的数量。

接下来 m m m 行,每行四个数: i , j , C i j , C j i i,j,C_{ij},C_{ji} i,j,Cij,Cji

输出

输出文件包括一行,及最小的环需要经过的房间数。

保证不会出现自环及重边。

样例输入

4 4
1 2 -10 3
1 3 1 -10
2 4 -10 -1
3 4 0 -3

样例输出

4

样例解释

1 → 3 → 4 → 2 → 1 1\rightarrow 3\rightarrow 4\rightarrow 2\rightarrow 1 13421 为最小的符合题意的环长度为 4. 4. 4.

数据范围

30 % 30\% 30% 的数据, n < = 10 ; n<=10; n<=10;
60 % 60\% 60% 的数据,, n < = 100 ; n<=100; n<=100;
100 % 100\% 100% 的数据, n < = 300 ; n<=300; n<=300;

思路

这道题就是爆搜加剪枝。

就枚举第一个碰到环的位置,然后直接跑 dfs ,如果跑回了第一个碰到环的位置就表示形成了环。在跑的同时维护路径的权值和,如果环的权值和大于 0 0 0 ,就拿答案与环的大小取最小值。

但是这样会超时,要剪枝:

  1. 当当前链的长度已经超过答案,就直接退出(这个很显然)
  2. 当权值和变成负的,就立刻退出。
    (因为如果这个环的边的权值和大于 0 0 0 ,那必然存在一个开始的点,使在转了一圈的全部过程的时候,权值和都保持大于 0 0 0 的状态)

然后好像就没了。

代码

#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

struct node {
	int x, to, nxt;
}e[100001];
int n, m, w, x, y, z, le[301], KK;
int dep[301], ans = 2147483647;
bool in[301];

void add(int x, int y, int z) {
	e[++KK] = (node){z, y, le[x]}; le[x] = KK;
}

void dfs(int now, int num, int l, int yeye) {
	if (l < 0 || num > ans) return ;//剪枝
	if (now == yeye && num && l > 0) {//有环
		ans = min(ans, num);
		return ;
	}
	in[now] = 1;
	for (int i = le[now]; i; i = e[i].nxt)
		if (!in[e[i].to] || e[i].to == yeye)
			dfs(e[i].to, num + 1, l + e[i].x, yeye);
	in[now] = 0;
}

int main() {
	scanf("%d %d", &n, &m);//读入
	for (int i = 1; i <= m; i++) {
		scanf("%d %d %d %d", &w, &x, &y, &z);//读入
		if (y + z > 0) {//可以在两条路中反复横跳
			printf("2");
			return 0;
		}
		add(w, x, y);//建图
		add(x, w, z);
	}
	
	for (int i = 1; i <= n; i++) {//枚举环中第一个遇到的点
		memset(in, 0, sizeof(in));
		dfs(i, 0, 0, i);
	}
	
	printf("%d", ans);//输出
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值