好久没更新公众号和博客了,因为最近在研究新的方向,所以很少发文。 笔者接触编程只有一年,这一年间主要研究启发式算法在运筹学中的应用。但是由于编程基础薄弱,在进一步研究复杂运筹学问题时发现基础算法不过关导致写出的代码运行速度很慢,因此很苦恼。所以决定这个暑假补习一下基础算法,主要是刷一些简单的ACM入门题。偶尔会发一些刷题笔记(偶尔!)。和作者有类似目标的同学可以一起交流共勉!
目前在看的教程: 北京理工大学ACM冬季培训课程
算法竞赛入门经典/刘汝佳编著.-2版可以在这里下载->github
课程刷题点 Virtual Judge
刷题代码都会放在github上,欢迎一起学习进步!
今天开始第二章的习题,然后简单看了下紫书第五章,主要讲的是C++STL的几个基础容器,也简单做下笔记。打算再Day的模拟与暴力15道题刷题期间把第五章的题目也刷几道。
第二章模拟与暴力刷题点,提交密码20202020
这题课堂上讲过思路,没看过的同学可以看昨天我的笔记。主要难点在于判断space、\n、\t,思路中提到通过去除这些之后进行对比,我就老实的按这个思路写代码了。
要解决的点主要是两个,一个是从普通的string到没有三个符号的string,这里通过遍历string,对相应字符执行earse函数删除。
第二个点比较麻烦,主要是输入的问题。输入可能有多行输入,题目的Example中就有一个第二行为空行的输入,这个用一般的cin、getline可能比较难获取。看到一个AC是用getchar获取的。
本来想用个偷懒的方法(见代码),但是好像WA了…算了懒得改了…
#include <iostream> #include <vector> #include <cstring> #include <algorithm> #include <cstdio> #include <string> using namespace std; #define LOCAL string getNoSpaceStr(string str) { string s = str; for (string::iterator it = s.begin(); it != s.end(); it++) { if (*it == ' ' || *it == '\n' || *it == '\t') { s.erase(it); it--; } } return s; } int main() { #ifdef LOCAL freopen("data.in", "r", stdin); // freopen("data.out", "w", stdout); #endif int kase; scanf("%d", &kase); cin.get(); while (kase-- > 0) { string start, end, real_ans; getline(cin, start); getline(cin, real_ans); getline(cin, end); while (end != "END") { real_ans += end + '\n'; getline(cin, end); } string start2, end2, user_ans; getline(cin, start2); getline(cin, user_ans); getline(cin, end2); while (end2 != "END") { user_ans += end2 + '\n'; getline(cin, end2); } string real_ans2 = getNoSpaceStr(real_ans); string user_ans2 = getNoSpaceStr(user_ans); if (real_ans == user_ans) { cout << "Accept" << endl; } else { if (real_ans2 == user_ans2) cout << "Presentation Error" << endl; else cout << "Wrong Answer" << endl; } } return 0; }一开始看到题目就觉得特别有规律,就这Example解释一下我的感觉: 题目中的swap有两种:本串内前后对称交换;两串间对应位置交换。那么对每个字符,有联系的字符只有四个,且可以任意互换位置,如"abacaba"的’a’和’a’以及"bacabaa"的’b’和’a’,四个之间可以相互交换。那么要达成两串完全相同的目的,在“三个字母相同,一个字母不同”的情况下必定要换掉一个字母。
那么对于两个字母相同、另外两个字母也相同的情况,或者其他情况,又是怎么样呢?可以进一步分析。注意替换的字母只能是第一串中的字母。下面给出我的思路的代码,判断相同时需要用下set(或map)(自我感觉实现的不太好):
#include <iostream> #include <vector> #include <cstring> #include <algorithm> #include <cstdio> #include <string> #include <set> using namespace std; // #define LOCAL int main() { #ifdef LOCAL freopen("data.in", "r", stdin); // freopen("data.out", "w", stdout); #endif int len; scanf("%d", &len); string str1, str2; cin >> str1 >> str2; int move = 0; len--; //skip'\0' for (int i = 0; i < len / 2; i++) { char a1, a2, b1, b2; a1 = str1[i]; a2 = str1[len - i]; b1 = str2[i]; b2 = str2[len - i]; set<char> s; s.insert(a1); s.insert(a2); s.insert(b1); s.insert(b2); int num = s.size(); if (num == 4) move += 2; //四个都不一样 else if (num == 3) { //两个不一样 if (a1 == a2 || b1 == b2) move += 2; else move++; } else if (num == 2) { //一个和别的不一样 int i = 0; if (a1 == a2) i++; if (a1 == b1) i++; if (a1 == b2) i++; if (i == 2) move++; } } if (len % 2 == 0) if (str1[len / 2] != str2[len / 2]) move++; cout << move; return 0; }然而又WA了… 于是去看答案,发现大家都是在模拟,这才发现本章是模拟章节…
就放一个网上找来的AC的模拟解法吧…
#include <cstdio> #include <cstring> #include <map> #include <algorithm> using namespace std; char s[100010], t[100010]; int main() { int n, i, j; scanf("%d", &n); scanf("%s%s", s, t); int ans = 0; for (i = 0; i < n / 2; i++) { map<char, int> v; //判断有几种字符,并记录各自个数 v[s[i]]++; v[t[i]]++; v[s[n - i - 1]]++; v[t[n - i - 1]]++; if (v.size() >= 2) { if (v.size() == 2) //有两种字符 { if (v[s[i]] != 2) //(1,3)情况 ans++; } else if (v.size() == 3) //有三种字符 { if (s[i] == s[n - i - 1]) // 因为是先改变再交换,且只能改变s字符串,所以这种情况下要改变两个 ans += 2; else //反之,改变一个 ans++; } else //有四种字符 ans += 2; } } if (n % 2) //奇数 if (s[n / 2] != t[n / 2]) //判断中间的两个字符是否相等 ans++; printf("%d\n", ans); }又是课堂上讲到的题目。通过几个给定的天平称量结果,找出重量不同的一枚假币。
首先等号两边出现过的都是真币,其次,假币出现在不等式里偏轻或偏重的次数(二者之一,不可能一会儿轻一会儿重)等于不等号出现的次数。
不过…额,今天好像写了三个WA…
嘿嘿嘿…
#include <iostream> #include <vector> #include <cstring> #include <algorithm> #include <cstdio> #include <string> #include <set> using namespace std; // #define LOCAL int main() { #ifdef LOCAL freopen("data.in", "r", stdin); // freopen("data.out", "w", stdout); #endif int n, m; //n为硬币数量,m为天平数量 scanf("%d%d", &n, &m); int *coins = new int[n + 5]; int *lightCoins = new int[n + 5]; int *heavyCoins = new int[n + 5]; memset(coins, 0, sizeof(int) * (n + 5)); memset(lightCoins, 0, sizeof(int) * (n + 5)); memset(heavyCoins, 0, sizeof(int) * (n + 5)); int unequalNr = 0; while (m-- > 0) { int i; cin >> i; int *left = new int[i + 5]; int *right = new int[i + 5]; for (int j = 0; j < i; j++) cin >> left[j]; for (int j = 0; j < i; j++) cin >> right[j]; char res; cin >> res; if (res == '=') { for (int j = 0; j < i; j++) { coins[left[j]] = 1; coins[right[j]] = 1; } } else if (res == '>') { unequalNr++; for (int j = 0; j < i; j++) { lightCoins[right[j]]++; heavyCoins[left[j]]++; } } else { unequalNr++; for (int j = 0; j < i; j++) { lightCoins[left[j]]++; heavyCoins[right[j]]++; } } } // for (int i = 1; i <= n; i++) // { // cout << coins[i] << " "; // } // cout << endl; // for (int i = 1; i <= n; i++) // { // cout << lightCoins[i] << " "; // } // cout << endl; // for (int i = 1; i <= n; i++) // { // cout << heavyCoins[i] << " "; // } int sum = 0; for (int i = 1; i <= n; i++) { if (!coins[i] && (lightCoins[i] == unequalNr || heavyCoins[i] == unequalNr)){ cout << i << endl; sum++; } } if(!sum) cout << '0' << endl; return 0; }写了这么多WA,就多读点书…
记了几个知识点分享一下。
读代码就是最好的学习方式,直接上代码: 不放题目了,放了也看不懂…(再次吐槽一下紫书题目描述实在太草)
以前只知道const int INF = 100000,最近频繁看到const,就百度了一下用法: 具体几个用法:
一个点,用vector作为返回值最好用引用(其实平时写运筹都这样做的,因为代码量大,循环多,任何一点操作对时间影响是很大的): 简单给个优先队列的定义,以前比较少接触:
random凑进来,一个知识点,取随机整数最好用rand.nextInt * 范围: assert用来纠错,及时停止,以后可以试着用下,不过一般有问题还是debug了吧…
过两天参加的比赛都要进入下一步了,刷题时间变少,会慢一点刷。
然后还要看python课…难顶…