HDU1317 变形SPFA 求最大路 判断正环 点权替代边权 最短路求法灵活应用

1)因为不理解在这个题的环境下如何正确处理正环的问题,做了一下午,其中核心的两句代码已经在code中标出,并在code下面举出极端数据的例子。如果我理解的不对请指出。

链表存储边的形式:

// 变形 spfa 求最大路,判断正环,点权替代边权,链表存储边
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <queue>
#include <stack>
#include <cstdio>
using namespace std;
const int maxn=120;
const int maxm=10010;
//const int INF=0x3f3f3f3f;//10^9
int visted[maxn];//是否在队列中
int room[maxn];//到达每个房间时,人物拥有的能源
int head[maxn];//从该点出发的最后一条路径
int cnt[maxn];//记录该点进入队列几次,如果超过n次,则说明出现正环路(因为这是求最长路,所以负环路不会被选择)将这样的点的visted设置一下,不让其再进队列即可
int energe[maxn];//记录每个房间的能量
int len;
struct Edge{//不用链表,用vector<int> Edge[maxn],vector构建二维数组也比较方便(Edge[i][j],第i个点的第j条出路是指向第几个节点,Edge(i).pushback(v);即可)
	int from;
	int to;
	int next;//该边的出发点的,出发的上一条边
}edge[maxm];
void add(int from,int to){
	edge[len].to=to;
	//edge[len].energe=energe;
	edge[len].next=head[from];
	head[from]=len;
	len++;
}
int SPFA(int star,int n){
	queue <int> q;
	q.push(star);
	visted[star]=1;
	cnt[star]++;
	while(!q.empty()){
		int cur=q.front();
		q.pop();
		visted[cur]=0;
		
		//判断正环的的两个核心语句,不太容易理解,已将极端数据的例子在code下方表示出来:
		if(cnt[cur]>n){//不能写cnt[cur]==n,会超时。假如已经cnt[cur]==n,使得room[cur]=10010了,然后cur房间对周围房间a、b的数据进行更新,假设a进入队列,a指向cur,对cur进行数据更新,cur会被更新,然后cur进入队列同时cnt[cur]++,此时的cnt[cur]==n+1,cur出队列,执行continue,跳过后续操作,直接从队列中取队首b出来并且进入while的下一次循环,但如果b也指向到cur房间的,那么cnt[cur]>n+1,就不会continue了,结果就是cur又对周围房间进行更新并且进入队列,然后cur指向a,a指向cur,进很多遍队列,直到另一条路走到n的路终于走到n才结束
			continue;
		}
		if(cnt[cur]==n){
			room[cur]=10010;//因为从一个房间到另一个房间所能能积攒的最大的能量是100*100=10000,当这个点进入正环,那么它会不断在环中积攒到达自己房间时的能量,再来对它周围的其他的房间产生收益,而房间n在极端的情况下恰恰是当这个房间积攒到最大能量的时候,为了不超时,我们人工将其赋值为无穷大然后让他最后一次更新周围的房间最大值,在它下一次从队列中出来,我们直接continue进入while的下一次循环从队列中拿出下一个房间的点来,让他跳过与周围房间比较更新周围房间的最大值以及判断是否进队列这一步即可。
		}
		
		
		for(int i=head[cur];i!=-1;i=edge[i].next){
			int to=edge[i].to;
			if(room[to]<room[cur]+energe[to]){
				room[to]=room[cur]+energe[to];
				if(to==n){
					return 1;
				}
				//int temp=room[to];
				//int bianhao=to;
				if(visted[to]==0){
					q.push(to);
					visted[to]=1;
					cnt[to]++;
				}
			}
		}
	}
	return 0;
}
int main()
{
	int n;
	while(~scanf("%d",&n)&&n!=-1){
		len=0;
		int star=1;
		memset(head,-1,sizeof(head));
		memset(room,0,sizeof(room));//如果用负无穷也可以,在SPFA中的松弛过程中加一个是否大于0的条件
		memset(visted,0,sizeof(visted));
		memset(cnt,0,sizeof(cnt));
		int from;
		int to;
		for(int i=1;i<=n;i++){
			cin>>energe[i];
			int v_n;
			cin>>v_n;
			from=i;
			while(v_n--){
				cin>>to;
				add(from,to);
			}
		}
		room[1]=100;//是从1开始!!

		if(SPFA(star,n)){
			cout<<"winnable"<<endl;
		}
		else{
			cout<<"hopeless"<<endl;
		}
	}
	return 0;
}

超时例:

房间号:1,2,3,4,5,6......n(其中n是目标房间,1是初始房间)

1->2,权值-99

2->3,权值+1        

2->4,权值-100

3->2,权值+1

4->2,,权值+1

4->5,权值-100

5->6,权值-100

6->.....->n  均为权值-100

那么:

队列里将会,1 2 3 4 2 2 5 3 4 3 4 2 2 5 ...进行了很多不必要的循环,当我们测得2与3之间形成了正环回路,我们直接将2赋值为无穷大(根据本题要求,房间最多100个,第一个和最后一个都是0能源,每个房间权值最低-100,那么一个房间到另一个房间的消耗的极限最小值不会小于-10000,所以把2赋值10000就够用了),然后让2更新周围的房间数据一次之后,遇到2就跳过,不再进行2与周围房间的比较也不让2进入队列,就会减少很多不必要的查找,从而让耗费的时间变小

2)
其他人的code,用vector<int> m[maxn]存储边,也很方便,如下

//不定长vector构建二维数组存储边
#include <stdio.h>
#include <iostream>
#include <set>
#include <map>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#define LL long long
#define _LL __int64
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 110;

int n;
int dis[maxn];
int inque[maxn];
int out[maxn];
vector <int> edge[maxn];
int w[maxn];

bool spfa()
{
    queue <int> que;
    memset(dis,0,sizeof(dis));
    memset(inque,0,sizeof(inque));
    memset(out,0,sizeof(out));

    inque[1] = 1;
    dis[1] = 100;
    que.push(1);

    while(!que.empty())
    {
        int u = que.front();
        que.pop();
        inque[u] = 0;
        out[u]++;

        if(out[u] > n) //在环中不再进队列
          continue;
        if(out[u] == n) //将dis置为INF
            dis[u] = INF;

        for(int i = 0; i < (int)edge[u].size(); i++)
        {
            int v = edge[u][i];
            if(dis[v] < dis[u] + w[v] )//注意中间也要保证>0。这里我在原code上稍加改动,因为memset初始化时,将dis[v]=0而不是=-INF,就已经替代了if(dis[v]<dis[u]+w[v]&&dis[u]+w[v]>0)
            {
                dis[v] = dis[u] + w[v];
                if(v == n) //能够到达n返回true
                    return true;
                if(inque[v]==0)
                {
                    inque[v] = 1;
                    que.push(v);
                }
            }
        }
    }
    return false;
}

int main()
{
    int num,v;
    while(~scanf("%d",&n))
    {
        if(n == -1) break;

        for(int i = 1; i <= n; i++)
        {
            edge[i].clear();
            scanf("%d",&w[i]);
            scanf("%d",&num);
            while(num--)
            {
                scanf("%d",&v);
                edge[i].push_back(v);
            }
        }

        if(spfa())
            printf("winnable\n");
        else printf("hopeless\n");
    }
    return 0;
}

3)

XYZZY

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 4146    Accepted Submission(s): 1146

Problem Description

It has recently been discovered how to run open-source software on the Y-Crate gaming device. A number of enterprising designers have developed Advent-style games for deployment on the Y-Crate. Your job is to test a number of these designs to see which are winnable.
Each game consists of a set of up to 100 rooms. One of the rooms is the start and one of the rooms is the finish. Each room has an energy value between -100 and +100. One-way doorways interconnect pairs of rooms.

The player begins in the start room with 100 energy points. She may pass through any doorway that connects the room she is in to another room, thus entering the other room. The energy value of this room is added to the player's energy. This process continues until she wins by entering the finish room or dies by running out of energy (or quits in frustration). During her adventure the player may enter the same room several times, receiving its energy each time.

Input

The input consists of several test cases. Each test case begins with n, the number of rooms. The rooms are numbered from 1 (the start room) to n (the finish room). Input for the n rooms follows. The input for each room consists of one or more lines containing:

the energy value for room i
the number of doorways leaving room i
a list of the rooms that are reachable by the doorways leaving room i
The start and finish rooms will always have enery level 0. A line containing -1 follows the last test case.

Output

In one line for each case, output "winnable" if it is possible for the player to win, otherwise output "hopeless".

Sample Input

 
 
5 0 1 2 -60 1 3 -60 1 4 20 1 5 0 0 5 0 1 2 20 1 3 -60 1 4 -60 1 5 0 0 5 0 1 2 21 1 3 -60 1 4 -60 1 5 0 0 5 0 1 2 20 2 1 3 -60 1 4 -60 1 5 0 0 -1

Sample Output

 
 
hopeless hopeless winnable winnable

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值