月度归档:2018年07月

洛谷 P1338 末日的传说

可以说是超难的一道题了。。。。
全程题解:
https://www.luogu.org/blog/user11765/solution-p1338

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<iostream>
#include<algorithm>
using namespace std;
long long n, m, a[50005];
int main() {
    cin >> n >> m;
    long long s = 1, e = n;
    for (long long i = 1; i <= n; i++) {
        long long t = (n - i)*(n - i - 1) / 2;
        if (t >= m)a[s++] = i;
        else {
            a[e--] = i;
            m -= e - s + 1;
        }
    }
    for (int i = 1; i <= n; i++)cout << a[i] << " ";
}

洛谷 P1582 倒水 题解

题目描述
一天,CC买了N个容量可以认为是无限大的瓶子,开始时每个瓶子里有1升水。接着~~CC发现瓶子实在太多了,于是他决定保留不超过K个瓶子。每次他选择两个当前含水量相同的瓶子,把一个瓶子的水全部倒进另一个里,然后把空瓶丢弃。(不能丢弃有水的瓶子)

显然在某些情况下CC无法达到目标,比如N=3,K=1。此时CC会重新买一些新的瓶子(新瓶子容量无限,开始时有1升水),以到达目标。

现在CC想知道,最少需要买多少新瓶子才能达到目标呢?

输入输出格式
输入格式:
一行两个正整数, N,K。

输出格式:
一个非负整数,表示最少需要买多少新瓶子。

这道题很有意思,所以要在这里好好的写一下题解。

由“开始时每个瓶子里有1升水”和“新瓶子容量无限,开始时有1升水”、“他选择两个当前含水量相同的瓶子,把一个瓶子的水全部倒进另一个里”,很容易联想到但凡不是空瓶子,每个瓶子里水的数量一定都是以2为底的幂,如1、2、4、8、16、32、64……等等。由此我们可以将这些数字放到二进制视角下来思考。

由题中说“他决定保留不超过K个瓶子”,可将其转化为“CC最终所具有的瓶子总数 n_f 在二进制表示形式下有总计不超过K个1”。
(下面说的所有东西可能都不太好想,请认真思考。)

然而我们需要买空瓶子……所以结果的数字只能大不能小,因此我们需要通过去除数字末尾的1,保留数字前面的1来使数字达到要求。

除了Lowbit之外,我还有一种做法(这个做法其实就是Lowbit和加综合起来的底层实现)。这个做法分为三步:
1、找到数字中倒数第一个1.
2、把接下来从后向前连续的所有1变成0.
3、再把接下来不是1的第一个0变成1.
举例:原数:001011
第二步完成后:001000
第三步完成后:001100
重复进行这一操作,直到满足要求“瓶子总数 n_f 在二进制表示形式下有总计不超过K个1”即可。

程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<string>
#include<deque>
#include<map>
#include<cstring>
using namespace std;
inline int & setBit(int & v, int sv, int p) {
    if (sv) {
        v |= 1 << p;
    }
    else {
        v &= ~(1 << p);
    }
    return v;
}
inline int getBit(int v, int p) {
    return (v >> p) & 1;
}
bool checkBits(int value, int num) {
    int cnt = 0;
    for (int i = 0; i < 32; i++) {
        cnt += getBit(value, i);
    }
    return cnt <= num;
}
int main() {
    int n, k;
    cin >> n >> k;
    int no = n;
    while (!checkBits(n, k)) {
        int ptr = 0;
        while (!getBit(n, ptr))ptr++;
        while (getBit(n, ptr)) {
            setBit(n, 0, ptr);
            ptr++;
        }
        setBit(n, 1, ptr);
    }
    cout << n-no;
}

洛谷 P1164 小A点菜

这动规没见过,不好做……参考题解了……
https://www.luogu.org/blog/DEDSECspace/solution-p1164
https://www.luogu.org/blog/6-28318530717958/solution-p1164

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<string>
#include<deque>
#include<map>
#include<cstring>
using namespace std;
int n, m;
int A[105];
int f[10005];
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++)cin >> A[i];
    f[0] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = m; j >= A[i]; j--) {
            f[j]+= f[j - A[i]];
        }
    }
    cout << f[m];
}

洛谷 P1060 开心的金明

Ver1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<string>
#include<deque>
#include<map>
#include<cstring>
using namespace std;
int n, m;
int v[30],w[30];//乘积,价格
int f[30][30005]; //物品,钱数
int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int tmp2;
        cin >> w[i] >> tmp2;
        v[i] = w[i] * tmp2;
    }
    for (int i = 1; i <= m; i++) {
        for (int j = 0; j <= n; j++) {
            if(j>=w[i])f[i][j] = max(f[i - 1][j], f[i - 1][j - w[i]] + v[i]);
            else f[i][j] = f[i - 1][j];
        }
    }
    int mmax = 0;
    for (int i = 0; i <= n; i++) {
        mmax = max(mmax, f[m][i]);
    }
    cout << mmax;
}

Ver2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<string>
#include<deque>
#include<map>
#include<cstring>
using namespace std;
int n, m;
int v[30],w[30];//乘积,价格
int f[30005]; //钱数
int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int tmp2;
        cin >> w[i] >> tmp2;
        v[i] = w[i] * tmp2;
    }
    for (int i = 1; i <= m; i++) {
        for (int j = n; j >=w[i]; j--) {
            f[j] = max(f[j], f[j - w[i]] + v[i]);
        }
    }
    int mmax=0;
    for (int i = 0; i <= n; i++) {
        mmax = max(mmax, f[i]);
    }
    cout << mmax;
}

洛谷 P1030 求先序排列

这类型的题(给中序和前后任一个,让求另外一个的)已经做了不下3、4遍了。。
再贴出来,代码风格可能会不大一样。。。应该是相比之前的有很多改进的。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<string>
#include<deque>
#include<map>
#include<cstring>
using namespace std;

struct node {
    char l, r;
    node() {
        l = r = '*';
    }
    node(char l1, char r1) {
        l = l1;
        r = r1;
    }
};
map<char, node> dic;

string in, post;

inline int searchInTraversal(char v, int s, int e) {
    for (int i = s; i <= e; i++)if (in[i] == v)return i;
    return -1;
}
char buildTree(int is, int ie,int ps,int pe) {
    char father = post[pe];
    if (is == ie && ps == pe) {
        dic[father] = node();
        return father;
    }
    if (is > ie || ps > pe) {
        return '*';
    }
    int posOfF = searchInTraversal(father, is, ie);
    int lTreeLen = posOfF - is;
    char l = buildTree(is, posOfF - 1, ps, ps + lTreeLen - 1);
    char r = buildTree(posOfF + 1, ie, ps + lTreeLen, pe - 1);
    dic[father] = node(l, r);
    return father;
}
void preTraversal(char node) {
    if (node == '*')return;
    cout << node;
    preTraversal(dic[node].l);
    preTraversal(dic[node].r);
}

int main() {
    cin >> in >> post;
    char f=buildTree(0, in.length() - 1, 0, post.length() - 1);
    preTraversal(f);
}

洛谷 P1886 滑动窗口

单调队列板子题。第一次新学,仍需继续努力练习。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/*

洛谷 P1886 滑动窗口 STL程序(各种优化都做的不大好,时间略长,但能AC)
“单调队列”数据结构 参考文档:
1、https://blog.csdn.net/a_bright_ch/article/details/77076228
2、https://blog.csdn.net/zhelong3205/article/details/77624699
3、https://blog.csdn.net/er111er/article/details/78344161
4、https://www.luogu.org/blog/hankeke/solution-p1886

建议真心想学的可以参考材料自己动手写一个。

*/

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<string>
#include<deque>
using namespace std;
int n, k;
int arr[1000005];
deque<int> dq,pq;
int main() {
    ios::sync_with_stdio(false);
    cin >> n >> k;
    for (int i = 1; i <= n; i++)cin >> arr[i];
    //单调队列求最小
    for (int i = 1; i <= n; i++) {
        //维护队尾并插入
        while (!dq.empty() && dq.back() > arr[i]) { //求最大和最小的区别在第二个条件是大于号还是小于号
            dq.pop_back();
            pq.pop_back();
        }
        dq.push_back(arr[i]);
        pq.push_back(i);
        //剔除无效队首并获得最优解
        while (!pq.empty() && pq.back() - pq.front() >= k) {
            dq.pop_front();
            pq.pop_front();
        }
        if (i >= k)cout << dq.front() << " ";
    }
    cout << endl;
    dq.clear();
    pq.clear();
    //单调队列求最大
    for (int i = 1; i <= n; i++) {
        //维护队尾并插入
        while (!dq.empty() && dq.back() < arr[i]) { //求最大和最小的区别在第二个条件是大于号还是小于号
            dq.pop_back();
            pq.pop_back();
        }
        dq.push_back(arr[i]);
        pq.push_back(i);
        //剔除无效队首并获得最优解
        while (!pq.empty()&&pq.back() - pq.front() >= k) {
            dq.pop_front();
            pq.pop_front();
        }
        if (i >= k)cout << dq.front()<< " ";
    }
   
}

洛谷 P1739 表达式括号匹配

有两个智障问题需要注意:
1、不要忘考虑 这种“))((”的情况。
2、还会有“(@)”的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    char c;
    int cnter = 0;
    while ((c = getchar())!='@') {
        if (c == '(')cnter++;
        if (c == ')')cnter--;
        if (cnter < 0) {
            cout << "NO";
            return 0;
        }
    }
    if (!cnter)cout << "YES";
    else cout << "NO";
}

洛谷 P1115 最大子段和

严格注意语句顺序不能改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<iostream>
#include<algorithm>
using namespace std;
int n;
int A[200005];
int mmax = 0x80000000;
int main() {
    ios::sync_with_stdio(false);
    cin >> n;
    for (int i = 1; i <= n; i++)cin >> A[i];
    int curSum = 0;
    for (int i = 1; i <= n; i++) {
        curSum += A[i];
        mmax = max(mmax, curSum);
        if (curSum < 0)curSum = 0;
    }
    cout << mmax;
}

洛谷 P1147 连续自然数和

需要用到等差数列的性质,再稍微减减枝就可以了。只要能想到这些,写出来没难度。
令 $a < b$ ,a为首项,b为末项,为等差数列,公差为1。 则有 $(a+b)*(b-a+1)/2=n$。 即 $(a+b)*(b-a+1)=2*n$。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
using namespace std;
int main() {
    long long n;
    cin >> n;
    for (long long a = 1; a <= n/2; a++) {
        for (long long b = a; b <= n/2+1; b++) {
            long long l = (a + b)*(b - a + 1);
            if (l == 2 * n)cout << a << " " << b << endl;
            if (l > 2 * n)break;
        }
    }
}

洛谷 字串变换

题号:P1032
不是特好写,用了Map。除此之外他题里面有一点没说清楚,也就是替换字符串的时候每个字符串不同位置各替换一次,示例:
Original String:”baaba”
Map:”a”=>”b”
Generated String:{“bbaba”,”babba”,”baabb”}
就是这样。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<functional>
using namespace std;
string A, B;
int n = 1;
string p[10][2];
queue<string> qs;
queue<int> qn;
map<string, bool> dic;
inline string replace(const string& origin, const string& toreplace, const string& replacement,int time) {
    string s = origin;
    int curpos = 0;
    time--;
    while (s.find(toreplace, curpos) != string::npos&&time) {
        curpos = s.find(toreplace, curpos)+toreplace.length();
        time--;
    }
    if (s.find(toreplace, curpos) != string::npos) {
        s.replace(s.find(toreplace, curpos), toreplace.length(), replacement);
        return s;
    }
    return "";
}
int main() {
    cin >> A >> B;
    while (cin >> p[n][0] >> p[n][1])n++;
    qs.push(A); qn.push(0);
    while (!qs.empty()) {
        string s = qs.front();
        int time = qn.front();
        qs.pop(); qn.pop();
        if (dic.count(s) || time > 10)continue;
        dic[s] = true;
        if (s == B) {
            cout << time;
            return 0;
        }
        for (int i = 1; i < n; i++) {
            string s2;
            for (int j = 1;; j++) {
                s2= replace(s, p[i][0], p[i][1],j);
                if (s2 == "")break;
                if (!dic.count(s2) && time + 1 <= 10) {
                    qs.push(s2); qn.push(time + 1);
                }
            }
        }
    }
    cout << "NO ANSWER!";
}