图算法
最短路径算法
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::vector
的push_back
操作比较慢,需要分配内存什么的,后来改成在char
数组全局变量,就过了。另外有个发现,使用std::ios::sync_with_stdio(false)
的cin cout
比printf 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 off
的cin
快。
#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,后面放上来