【最优高铁环】 分数规划,dfs判负环

1 篇文章 0 订阅
1 篇文章 0 订阅

牛客上此题链接.

这里所用的分数规划的公式是len1+len2+len3 = ans * 3
即 0 = ans - len1 + ans - len2 + ans - len3
用负环来判断这个公式中ans是否能够继续变大
原因是如果用ans - len 作为每一个边的权重,那么负环就是相当于得到 0 > ans - len1 + ans - len2 + ans - len3ans可以变大,所以就这样达到目的。
注意的是不能够把len - ans作为边的权重,即不能够把边权取相反数,因为上面判负环的情形是只要有负环存在,那么就能够获得更大的ans。 但是如果去了反之后, 就是只要有负环存在,ans就能够继续变小,通过这个判定无法得知当前ans是否能够变大,反而只是知道了ans是否能够变小,如果硬要用边权取反的形式来判断应该是需要保证有至少一个正环的情况下可以让ans继续变大。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define min(i, j) (i < j ? i : j)
#define max(i, j) (i > j ? i : j)
const int maxn = 5e5 + 60;
const int maxm = 5e5 + 60;

vector<int> G[maxn];
vector<int> l[maxn];

map<string, int> ma;

int orival[maxn];
char saveori[maxn];
char temp[maxn];

double dis[maxn];
int inqcnt[maxn];
int inq[maxn];
int idcnt = 0;
int vis[maxn];
int dfs(int s, double ans)
{
    vis[s] = 1;
    for (int i = 0; i < G[s].size(); i++)
    {
        int d = G[s][i];
        int len = l[s][i];
        //double reallen = (len * 1.0 / ans - 1);
        double reallen = len - ans;
        if (dis[d] > dis[s] + reallen)
        {
            dis[d] = dis[s] + reallen;
            if (vis[d] || dfs(d, ans))
                return 1;
        }
    }
    vis[s] = 0;
    return 0;
}

bool func(int s, double ans)
{
    for (int i = 1; i <= idcnt; i++)
    {
        dis[i] = 33333333333.3;
        vis[i] = 0;
    }
    for (int i = 1; i <= idcnt; i++)
    {
        if (dfs(i, ans)){
            // return 1;
        }
        else
        {
            return 1;
        }
    }
    return 0;
}

void solve()
{
    int n;
    cin >> n;
    int val = 0;
    for (int i = 1; i <= n; i++)
    {
        val = 0;
        scanf("%s", saveori + 1);
        int len = strlen(saveori + 1);
        int arr = 1;
        string s = "", t = "";
        while (arr <= len)
        {
            int temparr = 0;
            if (saveori[arr] == ' ')
            {
                arr++;
                continue;
            }
            while (saveori[arr] != '-' && saveori[arr] != '\n' && arr <= len)
            {
                temp[temparr++] = saveori[arr++];
            }
            arr++;
            if (temparr)
            {
                if (temp[0] == 'S')
                {
                    val += 1000;
                }
                else if (temp[0] == 'G')
                {
                    val += 500;
                }
                else if (temp[0] == 'D')
                {
                    val += 300;
                }
                else if (temp[0] == 'T')
                {
                    val += 200;
                }
                else if (temp[0] == 'K')
                {
                    val += 150;
                }
            }
            temp[temparr] = '\0';
            if (s.size() == 0)
            {
                s = string(temp);
            }
            t = string(temp);
        }
        if (!ma.count(t))
        {
            ma[t] = ++idcnt;
        }
        if (!ma.count(s))
        {
            ma[s] = ++idcnt;
        }
        G[ma[s]].push_back(ma[t]);
        l[ma[s]].push_back(val);
    }
    double r = 0x7fffffff;
    double l = 0.00000001;
    double ans = -1.0;
    while (r - l >= 0.0001)
    {
        double mid = (r + l) / 2.0;
        int res = func(1, mid);
        if (res == 0)
        {
            ans = mid;
            l = mid;
        }
        else
        {
            r = mid;
        }
    }
    // ans = (l + r) / 2;
    printf("%.0f\n", ans);
}

int main()
{
    // cout << (1500.0 / 1285 - 1.0) + (1500.0 / 1285 - 1.0) + (850.0 / 1285 - 1.0) << endl;
    freopen("in.txt", "r", stdin);
    int T;
    solve();
}
相关推荐

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页

打赏作者

Π鱼星先生

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值