2017-2018 ACM-ICPC German Contest (GCPC 2017) E Perpetuum Mobile (level 3)(spfa判环+log转换小数乘法)

题目链接

题意:

题面70%的都是废话,还有很多影响读题的条件...

其实就是给你一个有向图,问你这个有向图里面存不存在环,使得组成环的边的权值的总乘积>1

有输出"inadmissiable",否则输出"admissiable"

解析:

这道题其实就是一道spfa判负权环的题目,找最长路。

但是这里的小数很难处理,因为有4位小数,并且还有5000条边,乘积的话数据的数值和精度都会达到非常大

这里就有了神奇的log函数,可以使得乘法变成加法,除法变成减法,并且大小的相对顺序还不会发生改变

#include<vector>
#include<queue>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstdlib>
using namespace std;
#define pb push_back
typedef long long int ll;
typedef long double LD;
#define MAXN 900
#define INF 0x3f3f3f3f
const ll MOD =1000000007;
typedef struct node
{
    int v;
    LD w;
	node(int vv,LD ww):v(vv),w(ww){}
}node;
 
vector<node> edge[MAXN];
int n;
int vis[MAXN];
LD d[MAXN];
int r[MAXN];  //入队次数,如果一个点入队次数>=n,那么存在负环

int read(){
 
    char c=getchar();int x=0,f=1;
 
    while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
 
    while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
 
    return x*f;
 
}
queue<int> q;
int spfa(int src)
{
	while(!q.empty()) q.pop();
    for(int i=0;i<=n;i++) d[i]=-INF,vis[i]=0,r[i]=0;
    d[src]=0;
    
    q.push(src);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=0;i<edge[u].size();i++)
        {
            node x=edge[u][i];
            LD tmp=d[u]+x.w;
			//if(x.v==src&&tmp>d[src]) return 1;
            if(d[x.v]<tmp)
            {
                d[x.v]=tmp;
                if(!vis[x.v])
                {
					r[x.v]++;
                    vis[x.v]=1;
                    q.push(x.v);
                }
            }
			if(r[x.v]>=n) return 1;  //有环
        }
    }
	return 0;
}
 
int main()
{
    
    int m;
    int x,y;
	scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
		int u,v;
		LD w;
        scanf("%d%d%Lf",&u,&v,&w);
		edge[u].push_back(node(v,log(w)));
        
    }
	//for(int i=1;i<=n;i++) r[i]=0;
	int flag=0;
    for(int i=1;i<=n;i++)
	{
		{
			flag=spfa(i);
			if(flag) break;
		}
	}
	if(flag) printf("inadmissible\n");
	else printf("admissible\n");
    return 0;
 
}

用bellman-Ford判负环也可以

代码来源ChinaReconquista

#include <bits/stdc++.h>
using namespace std;

#define N 5010
int a[N], b[N];
long double c[N], d[N];

int main()
{
	int n, m;
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; ++i)
	{
		scanf("%d %d", &a[i], &b[i]);
		double w;
		scanf("%lf", &w);
		c[i] = log(w);
	}
	for (int i = 1; i <= n; ++i)
	{
		for (int j = 1; j <= m; ++j)
		{
			d[b[j]] = max(d[b[j]], d[a[j]] + c[j]);
		}
	}
	int flag = 0;
	for (int i = 1; i <= m; ++i)
	{
		if (d[b[i]] < d[a[i]] + c[i])
			flag = 1;
	}
	puts(flag? "inadmissible": "admissible");
	return 0;
}

然后下面就是我自己神奇的卡过方法.....

我发现这道题数据可能并没有卡你数值大小和精度,我直接用边相乘找最大路,判负环也过了。

一开始一直在WA,后来我把spfa判负环的条件改成了if(x.v==src&&tmp>d[src]) return 1;就成功卡过了。

这个可以的原因是,之前我是一个负环跑n边才判定找到。

这里不一样的是我是每一个点都跑一边spfa,那么对于每一个点,我只要找包含他的负环就可以了

#include<vector>
#include<queue>
#include<algorithm>
#include<cstdio>
using namespace std;
#define pb push_back
typedef long long int ll;
typedef long double LD;
#define MAXN 900
#define INF 0x3f3f3f3f
const ll MOD =1000000007;
typedef struct node
{
    int v;
    LD w;
	node(int vv,LD ww):v(vv),w(ww){}
}node;
 
vector<node> edge[MAXN];
int n;
int vis[MAXN];
LD d[MAXN];
int r[MAXN];  //入队次数,如果一个点入队次数>=n,那么存在负环

int read(){
 
    char c=getchar();int x=0,f=1;
 
    while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
 
    while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
 
    return x*f;
 
}
queue<int> q;
int spfa(int src)
{
	while(!q.empty()) q.pop();
    for(int i=0;i<=n;i++) d[i]=0,vis[i]=0,r[i]=0;
    d[src]=1;
    
    q.push(src);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=0;i<edge[u].size();i++)
        {
            node x=edge[u][i];
            LD tmp=d[u]*x.w;
			if(x.v==src&&tmp>d[src]) return 1;
            if(d[x.v]<tmp)
            {
                d[x.v]=tmp;
                if(!vis[x.v])
                {
					r[x.v]++;
                    vis[x.v]=1;
                    q.push(x.v);
                }
            }
			if(r[x.v]>=n) return 1;  //有环
        }
    }
	return 0;
}
 
int main()
{
    
    int m;
    int x,y;
	scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
		int u,v;
		LD w;
        scanf("%d%d%Lf",&u,&v,&w);
		edge[u].push_back(node(v,w));
        
    }
	//for(int i=1;i<=n;i++) r[i]=0;
	int flag=0;
    for(int i=1;i<=n;i++)
	{
		{
			flag=spfa(i);
			if(flag) break;
		}
	}
	if(flag) printf("inadmissible\n");
	else printf("admissible\n");
    return 0;
 
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值