zoukankan      html  css  js  c++  java
  • 题解【LOJ10082】「一本通 3.3 例 1」Word Rings

    题面

    一开始有一个很直观的想法:

    对于每一个单词,如果它能够与另外一个单词相匹配,就从当前单词向另外一个单词连一条长度为当前单词长度的边。

    但这样做最坏情况下会有 (10^5) 个点,(10^{10}) 条边,时间和空间都会承受不了。

    于是我们考虑如何优化建图。

    换一种建图的方式:

    对于每一个单词,将它的前两个字符和后两个字符之间连一条长度为当前单词的长度的边。

    稍微思考一下就会发现这种建图方式其实是和第一种等价的。

    建完图后,这个问题就变成了一个 (01) 分数规划问题,要求的是 (frac{sum 单词长度总和}{sum 单词个数}) 的最大值。

    然后就是一些基本操作:二分答案 (+) SPFA 找 正环

    注意 SPFA 时需要加一些优化:

    • 记录一下进行过松弛操作的点的数量,如果所有的点都已经做过了,就说明很大可能存在一个正环,直接返回 true;

    这样就可以通过此题了。

     #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 703, M = 200003;
    
    int n, m;
    int tot, head[N], ver[M], nxt[M], edge[M];
    double dist[N];
    int cnt[N];
    bool st[N];
    char s[1003];
    
    inline void add(int u, int v, int w)
    {
        ver[++tot] = v, edge[tot] = w, nxt[tot] = head[u], head[u] = tot;
    }
    
    inline bool check(double mid)
    {
        memset(cnt, 0, sizeof cnt);
        memset(st, false, sizeof st);
        queue <int> q;
        for (int i = 0; i <= 675; i+=1)
        {
            q.push(i); //先将所有点加进队列
            st[i] = true;
        }
        int cntt = 0;
        while (!q.empty())
        {
            int u = q.front(); q.pop();
            st[u] = false;
            for (int i = head[u]; i; i = nxt[i])
            {
                int v = ver[i], w = edge[i];
                if (dist[v] < dist[u] + w - mid)
                {
                    dist[v] = dist[u] + w - mid;
                    cnt[v] = cnt[u] + 1;
                    if (++cntt > 10000) return true; //进行过松弛操作的点的数量
                    if (cnt[v] >= N) return true; //存在正环
                    if (!st[v])
                    {
                        st[v] = true;
                        q.push(v);
                    }
                }
            }
        }
        return false; //没有正环
    }
    
    int main()
    {
        while (1)
        {
            cin >> n;
            if (!n) break;
            memset(head, 0, sizeof head);
            tot = 0;
            for (int i = 1; i <= n; i+=1)
            {
                scanf("%s", s + 1);
                int len = strlen(s + 1);
                if (len >= 2)
                {
                    int c1 = (s[1] - 'a') * 26 + (s[2] - 'a'), c2 = (s[len - 1] - 'a') * 26 + (s[len] - 'a');
                    add(c1, c2, len); //建图,注意是单向边
                }
            }
            if (!check(0)) {puts("No solution"); continue;}
            double l = 0.0, r = 1003.0;
            while (r - l > 1e-4)
            {
                double mid = (l + r) / 2.0;
                if (check(mid)) l = mid;
                else r = mid;
            }
            printf("%.2lf
    ", l);
        }
        return 0;
    }
    
  • 相关阅读:
    Linux下查看使用的是哪种shell的方法汇总【转】
    Shell中的while循环【转】
    shell浅谈之三for、while、until循环【转】
    cpu_relax( )-----对自选循环等待(spin-wait loops)操作的优化【转】
    Linux makefile 教程 非常详细,且易懂【转】
    链表常见的问题【转】
    linux内核同步之信号量、顺序锁、RCU、完成量、关闭中断【转】
    linux内核同步之每CPU变量、原子操作、内存屏障、自旋锁【转】
    关于hrtimer_forward小段代码的分析【转】
    CTP多点触摸协议【转】
  • 原文地址:https://www.cnblogs.com/xsl19/p/12402655.html
Copyright © 2011-2022 走看看