POJ 1860 3253 1035 2388 3349

图算法

最短路径算法

1860 18-07-30

CSDN blog的点拨,知道了这道题其实是求正环,只有存在正环,才能让资金不断得增长。我感觉正环可以分为两种(该正环的要求是其中不包含有正环),一种是不经过原有货币,一种是经过原有货币。第一种比如例题中,正环为2323232323…,另一种则比如123123123123…这样兑换。

正环存在的判断方法之一是存在某点作为松弛操作的基点超过 |V|1 | V | − 1 次,因为从普通的Bellman-Ford方法中可以知道,最短路不会经过同一个顶点两次,因此最多通过 |V|1 | V | − 1 条边,所以如果每次松弛都是对边进行遍历,则遍历次数不会超过 |V|1 | V | − 1 次,因此可以推出SPFA中对点进行松弛操作的上界也为 |V|1 | V | − 1 次。

SPFA方法参见CSDN blog,比较好理解,就是对BF方法的优化。

#include <cstdio>
#include <vector>
#include <queue>
const int N = 105;
int n, m, s;
double v;
std::vector<int> to[N];
std::vector<double> rate[N];
std::vector<double> commission[N];
std::queue<int> spfa_queue;
double dis[N] = {0};
bool used[N] = {0};
int cnt[N] = {0};
bool spfa(int s, double v)
{
    spfa_queue.push(s);
    used[s] = true;
    dis[s] = v;
    while (!spfa_queue.empty())
    {
        int head = spfa_queue.front();
        spfa_queue.pop();
        used[head] = false;
        ++cnt[head];
        if (cnt[head] > n)
            return true;
        for (int i = 0; i < to[head].size(); ++i)
        {
            if ((dis[head]-commission[head][i])*rate[head][i] > dis[to[head][i]])
            {
                dis[to[head][i]] = (dis[head]-commission[head][i])*rate[head][i];
                if (!used[to[head][i]])
                {
                    spfa_queue.push(to[head][i]);
                    used[to[head][i]] = true;
                }
            }
        }
    }
    // 其实这里的判断是不必要的,相当于就是上述的经过原点的正环
    if (dis[s] > v)
        return true;
    return false;
}
int main()
{
    scanf("%d%d%d%lf", &n, &m, &s, &v);
    int a, b;
    double ra, ca, rb, cb;
    while (m--)
    {
        scanf("%d%d%lf%lf%lf%lf", &a, &b, &ra, &ca, &rb, &cb);
        to[a].push_back(b);
        rate[a].push_back(ra);
        commission[a].push_back(ca);
        to[b].push_back(a);
        rate[b].push_back(rb);
        commission[b].push_back(cb);
    }
    if (spfa(s, v))
        printf("YES\n");
    else
        printf("NO\n");
}

哈夫曼树

3253 18-07-30

其实是因为看了大纲知道这个用哈夫曼做的。可以构想,如果一块木板是后切出来,其长度就要被多次计算,因此切这块木板的代价就是该节点的带权路径长度,所以可以用哈夫曼树来做。这道题卡了时间,一开始每次合并节点之后使用std::sort来进行排序,这样总复杂度为 O(n2logn) O ( n 2 l o g n ) ,TLE。由于我们每次合并只需要最小的两个数,所以可以使用遍历做,复杂度为 O(n2) O ( n 2 ) 。要注意的是得到的idx1 idx2要调整顺序,防止交换元素过程出现混乱(比如idx1=4, idx2=0

#include <cstdio>
#include <algorithm>
const int N = 20002;
int woods[N*2] = {0};
int main()
{
    int n, head = 0, rear;
    long long total = 0;
    scanf("%d", &n);
    rear = n;
    for (int i = 0; i < n; ++i)
        scanf("%d", woods+i);
    for (int i = 0; i < n-1; ++i)
    {
        int m1 = 2000000000, m2 = m1, idx1 = -1, idx2 = -1;
        for (int j = head; j < rear; ++j)
        {
            if (woods[j] < m2)
            {
                m2 = woods[j];
                idx2 = j;
            }
            if (m2 < m1)
            {
                std::swap(m1, m2);
                std::swap(idx1, idx2);
            }
        }
        if (idx1 > idx2)
            std::swap(idx1, idx2);
        std::swap(woods[head], woods[idx1]);
        std::swap(woods[head+1], woods[idx2]);
        woods[rear] = m1 + m2;
        total += woods[rear];
        head += 2;
        ++rear;
        /* for (int j = 0; j < rear; ++j) printf("%d ", woods[j]); printf("\n"); */
    }
    printf("%lld", total);
}

数据结构

1035 18-07-13

一开始以为会有什么技巧,但其实想了想数据量不是很大,并且输出要求按字典中的顺序,所以感觉应该还是遍历加上字符串之间的判断。一开始使用了std::string,TLE,感觉可能是因为std::vectorpush_back操作比较慢,需要分配内存什么的,后来改成在char数组全局变量,就过了。另外有个发现,使用std::ios::sync_with_stdio(false)cin coutprintf scanf快。更新发现,有些题卡输入即使sync off还是不如scanf快。

有大神使用hash来做的,后面研究一下。

// TLE code
#include <string>
#include <iostream>
#include <vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::vector<std::string> dict;
    std::string temp;
    while (std::cin >> temp)
    {
        if (temp == "#")
            break;
        dict.push_back(temp);
    }
    int dict_size = dict.size();
    while (std::cin >> temp)
    {
        if (temp == "#")
            break;
        bool correct = false;
        std::vector<std::string> cand;
        for (int i = 0; i < dict_size; ++i)
        {
            std::string comp = dict[i];
            int c_length = comp.length();
            int t_length = temp.length();
            if (comp == temp)
            {
                correct = true;
                break;
            }
            if ((c_length - t_length) < -1 || (c_length - t_length) > 1)
                continue;
            else if (c_length == t_length)
            {
                int error = 0;
                for (int j = 0; j < c_length; ++j)
                {
                    if (comp[j] != temp[j])
                        ++error;
                }
                if (error == 1)
                    cand.push_back(comp);
            }
            else if (c_length == t_length - 1)
            {
                int j = 0, k = 0;
                for (; k < t_length && j < c_length;)
                {
                    if (comp[j] != temp[k])
                        ++k;
                    else
                    {
                        ++k;
                        ++j;
                    }
                }
                if (j == c_length)
                    cand.push_back(comp);
            }
            else
            {
                int j = 0, k = 0;
                for (; k < t_length && j < c_length;)
                {
                    if (comp[j] != temp[k])
                        ++j;
                    else
                    {
                        ++k;
                        ++j;
                    }
                }
                if (k == t_length)
                    cand.push_back(comp);
            }
        }
        if (correct)
            std::cout << temp << " is correct" << std::endl;
        else
        {
            std::cout << temp << ": ";
            for (int i = 0; i < cand.size(); ++i)
                std::cout << cand[i] << " ";
            std::cout << std::endl;
        }
    }
}
// AC Code
// [cin cout] 407ms [scanf cout] 438ms [scanf printf] 485ms
#include <string.h>
#include <iostream>
#include <vector>
#include <cstdio>

char dict[10005][16];

int main()
{
    std::ios::sync_with_stdio(false);
    // std::string temp;
    int count = 0;
    while (std::cin >> dict[count])
    {
        if (dict[count][0] == '#')
            break;
        ++count;
    }
    // int dict_size = dict.size();
    char temp[16];
    while (std::cin >> temp)
    {
        if (temp[0] == '#')
            break;
        bool correct = false;
        std::vector<char *> cand;
        for (int i = 0; i < count; ++i)
        {
            // std::string comp = dict[i];
            char *comp = dict[i];
            // int c_length = comp.length();
            // int t_length = temp.length();
            int c_length = strlen(comp);
            int t_length = strlen(temp);
            if (strcmp(comp, temp) == 0)
            {
                correct = true;
                break;
            }
            if ((c_length - t_length) < -1 || (c_length - t_length) > 1)
                continue;
            else if (c_length == t_length)
            {
                int error = 0;
                for (int j = 0; j < c_length; ++j)
                {
                    if (comp[j] != temp[j])
                        ++error;
                }
                if (error == 1)
                    cand.push_back(comp);
            }
            else if (c_length == t_length - 1)
            {
                int j = 0, k = 0;
                for (; k < t_length && j < c_length;)
                {
                    if (comp[j] != temp[k])
                        ++k;
                    else
                    {
                        ++k;
                        ++j;
                    }
                }
                if (j == c_length)
                    cand.push_back(comp);
            }
            else
            {
                int j = 0, k = 0;
                for (; k < t_length && j < c_length;)
                {
                    if (comp[j] != temp[k])
                        ++j;
                    else
                    {
                        ++k;
                        ++j;
                    }
                }
                if (k == t_length)
                    cand.push_back(comp);
            }
        }
        if (correct)
            std::cout << temp << " is correct" << std::endl;
        else
        {
            std::cout << temp << ": ";
            for (int i = 0; i < cand.size(); ++i)
                std::cout << cand[i] << " ";
            std::cout << std::endl;
        }
    }
}

排序

2388 18-07-19

这道题真的也太水了,直接用STL的sort排了就能过,但是精益求精,查阅博客之后看到求中位数的过程,可以利用快排的思想,但是并不需要对整个数组进行排序,只需要在每次快排之后,检查当前停止点,即i == j时,i是否为(n-1)/2,如果不是,则包含了(n-1)/2的点的那一边就行排序,另一边就不用管了。要注意的是,对于取首元素作为标准的情况来说,要把--j的循环放在前面,因为这样才能保证标准元素的位置被不断移动,同理,如果取最后一个元素,则把++i放在前面。这样做之后,从使用STL的94ms提升到了47ms。

#include <iostream>
#include <algorithm>
int milk[1000005];
int n;
int arrange(int head, int rear)
{

    int i = head, j = rear-1, key = milk[head];
    while (i != j)
    {
        while (i != j && milk[j] >= key)
            --j;
        std::swap(milk[i], milk[j]);
        while (i != j && milk[i] <= key)
            ++i;
        std::swap(milk[i], milk[j]);
    }
    if (i == n/2)
        return milk[i];
    else if (i > n/2)
        return arrange(head, i);
    else
        return arrange(i+1, rear);
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin >> n;
    for (int i = 0; i < n; ++i)
        std::cin >> milk[i];
    std::cout << arrange(0, n) << std::endl;
}

哈希

3349 18-07-19

这道题使用哈希的原因主要还是初步筛选可能相同的雪花,因此使用和作为哈希的基础,根据一篇博客,哈希key需要使用接近100w的数,但至于是否一定要使用素数,应该是不必要的。因此所有可能相同的雪花都会存在同一个哈希地址中,使用链表来解决同地址元素的存储。雪花的比较可以是顺指针也可以是逆时针,并且比较的时候要进行一定旋转,才能比较完整。这道题同时也卡了输入,与之前的发现所不同的是,scanf依然要比sync offcin快。

#include <iostream>
#include <cstring>
typedef struct
{
    int data[6];
    int next;
} node;
int hashtable[1000000];
int prime = 999983;
node nodes[100005];
bool clockwise(int aidx, int bidx)
{
    node na = nodes[aidx], nb = nodes[bidx];
    for (int i = 0; i < 6; ++i)
    {
        int a = 0, b = i;
        bool found = true;
        for (int j = 0; j < 6 && found; ++j)
        {
            if (na.data[a] != nb.data[b])
                found = false;
            ++a;
            b = (b + 1) % 6;
        }
        if (found)
            return true;
    }
    return false;
}
bool anticlockwise(int aidx, int bidx)
{
    node* na = nodes+aidx, *nb = nodes+bidx;
    for (int i = 0; i < 6; ++i)
    {
        int a = 0, b = i;
        bool found = true;
        for (int j = 0; j < 6 && found; ++j)
        {
            if (na->data[a] != nb->data[b])
                found = false;
            ++a;
            b = (b + 5) % 6;
        }
        if (found)
            return true;
    }
    return false;
}
bool test_hash(int nidx, int hidx)
{
    int test = hashtable[hidx];
    while (test >= 0)
    {
        if (clockwise(nidx, test) || anticlockwise(nidx, test))
            return true;
        test = nodes[test].next;
    }
    return false;
}
bool insert_hash(int idx)
{
    int sum = 0;
    for (int i = 0; i < 6; ++i)
        sum = (sum + nodes[idx].data[i]) % prime;
    int hash_idx = sum % prime + 1;
    if (hashtable[hash_idx] < 0)
    {
        hashtable[hash_idx] = idx;
        return false;
    }
    else if(test_hash(idx, hash_idx))
        return true;
    else
    {
        nodes[idx].next = hashtable[hash_idx];
        hashtable[hash_idx] = idx;
        return false;
    }
}
int main()
{
    std::ios::sync_with_stdio(false);
    // std::memset(hashtable, 0x80, sizeof(hashtable));
    int n, a, b, c, d, e, f;
    std::cin >> n;
    bool found = false;
    for (int i = 0; i < n; ++i)
    {
        if (!found)
        {
            for (int j = 0; j < 6; ++j)
                std::cin >> nodes[i].data[j];
            nodes[i].next = -1;
            found = insert_hash(i);
        }
        else
            std::cin >> a >> b >> c >> d >> e >> f;
    }
    if (found)
        std::cout << "Twin snowflakes found." << std::endl;
    else
        std::cout << "No two snowflakes are alike." << std::endl;
}

有一次因为特定需要,于是研究了HDUOJ的5311,后面放上来

点赞