算法竞赛入门经典(第2版)—第八章(高效算法设计)

零碎知识点
  • / 表示除法,在C++中是向零方向取整,比如5/2=2、(-5)/2=-2。因此在表示区间[a, b]的中点时,可以写成mid = a+(b-a)/2来代替mid = (a+b)/2,因为第一种方法保证中点总是靠近区间起点,使得中点保持了一致性。
题目
120 - Stacks of Flapjacks

题目链接:120 - Stacks of Flapjacks

  • 题目大意:题目解释见解释
  • 思路:本道题是技巧题。思路参考选择排序,但是按从大到小排序,每一次选择出未排序部分的最大值,记录其下标,判断其是否已经在排好序的位置,如果没有则将其翻转到最上面(如果已经在最上面则忽略此步),然后将其翻转到对应排序位置,如此循环计算即可。

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <sstream>
#include <vector>
using namespace std;

const int MAX = 101;
int a[MAX], x;
string s;
vector<int> ans;

void Reverse(int n)
{
    int l = 0, r = n;
    while(r>l)
    {
        swap(a[r], a[l]);
        r--, l++;
    }
}
int main()
{
    while(getline(cin, s))
    {
        ans.clear();
        stringstream ss(s);
        int k = 0;
        while(ss >> x)
        {
            a[k++] = x;
        }
        for(int i=0; i<k; i++)
        {
            if(i) printf(" ");
            printf("%d", a[i]);
        }
        printf("\n");
        int cur = k-1;//当前元素应该翻转到的位置
        while(cur)
        {
            int Max = -1, idx = -1;
            for(int i=0; i<=cur; i++)
            {
                if(Max<a[i])
                    Max = a[i], idx = i;
            }
            if(idx!=0 && idx!=cur)//将需要排序且未排序的翻转为头
            {
                Reverse(idx);
                ans.push_back(k-idx);
            }
            if(idx!=cur)//将在头部需要排序的翻转为对应位置
            {
                Reverse(cur);
                ans.push_back(k-cur);
            }
            cur--;
        }
        for(int i=0; i<ans.size(); i++)
        {
            printf("%d ", ans[i]);
        }
        printf("0\n");
    }
    return 0;
}
1605 - Building for UN

题目链接:1605 - Building for UN
参考博文:UVA - 1605 Building for UN (构造法)

  • 题目大意:一个联合国大楼每层都有数量相等大小相同的格子,将其分配给n个国家,使任意两个不同的国家都相邻,即同层有公共边或相邻层的同一个格子。
  • 思路:直接构造符合题意的答案。分成两层,每层 n * n 个格子,第一层第 i 行全部为国家 i ,第二层第 j 列全部为国家 j 。

代码:

#include <bits/stdc++.h>
using namespace std;

char a[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; //定义国家对应的符号
int main()
{
    int n;
    while(scanf("%d", &n)!=EOF)
    {
        printf("%d %d %d\n", 2, n, n);
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
                printf("%c", a[i]);
            printf("\n");
        }
        printf("\n");
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
                printf("%c", a[j]);
            printf("\n");
        }
        printf("\n");
    }
    return 0;
}
1152 - 4 Values whose Sum is 0

题目链接:1152 - 4 Values whose Sum is 0

  • 题目大意:给你四个集合,让你从每个集合中挑选一个数字使他们的和为0,问共有多少种方法。
  • 思路:将前两个集合中所有组合的和存储起来为b数组,排序之后,在后两个集合中的组合中查找与b数组中值相同的数目(注意用二分),累加该数目即可。

代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int MAX = 10000;
LL a[5][MAX];
int n;
vector<LL> b;

int main()
{
    int T, kase = 0;
    scanf("%d", &T);
    while(T--)
    {
        b.clear();
        scanf("%d", &n);
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<4; j++)
                scanf("%lld", &a[j][i]);
        }
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
            {
                b.push_back(a[0][i]+a[1][j]);
            }
        }
        sort(b.begin(), b.end());
        LL cnt = 0;
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
            {
                LL c = -a[2][i]-a[3][j];
                LL t = upper_bound(b.begin(), b.end(), c) - lower_bound(b.begin(), b.end(), c);
                cnt += t;
            }
        }
        if(kase++) printf("\n");
        printf("%lld\n", cnt);
    }
    return 0;
}
11054 - Wine trading in Gergovia

题目链接:11054 - Wine trading in Gergovia

  • 题目大意:有N户人家住在一条街上,每户人家有需求和供应葡萄酒, 因为路程的不同,导致交易葡萄酒的成本不同,成本等于交易量* 路程,求,最少的交易成本使得每户人家的需求和供应的到满足,(总需求= 总供应)。
  • 思路:贪心的思想, 最左边的人家想要获得或者是卖出,一定是对右边的人家进行操作,所以问题可以转化成第二家人的需求变成num[0] + num[1], 而运输量即为num[0]的绝对值,不管第二户人家的需求与第一户人家的需求是否匹配或者数量够不够,都可以看成是人家1在买卖过程中将货物暂时放在人家2,以此类推到最后一户人家。

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;

typedef long long LL;

int main()
{
    LL n, a, sum, cur;
    while(scanf("%lld", &n) && n)
    {
        sum = 0, cur = 0;
        for(int i=0; i<n; i++)
        {
            scanf("%lld", &a);
            sum += abs(cur);//累加转移的就量
            cur += a;//获得当前的转移酒量
        }
        printf("%lld\n", sum);
    }
    return 0;
}
11572 - Unique Snowflakes

题目链接:11572 - Unique Snowflakes

  • 题目大意: 输入一个长度为n的序列A,找到一个尽量长度 连续子序列 A L {A}_L AL- A R {A}_R AR,使得该序列中没有相同的元素。
  • 思路:不断更新区间的起始和终点,如果新加入的元素在区间内有重复元素则更新起始点到重复元素的下一个位置。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;

map<int, int> m;

int main()
{
#ifdef ONLINE_JUDGE
#else
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
#endif
    int n, T, a;
    scanf("%d", &T);
    while(T--)
    {
        m.clear();
        int st = 0, ed = 0, Max = 0;
        scanf("%d", &n);
        for(int i=0; i<n; i++)
        {
            scanf("%d", &a);
            if(m.count(a) && m[a]>=st)//当输入的a与[st,ed)区间内的一个字符重了,更新st
            {
                st = m[a]+1;
            }
            m[a] = ed;//每输入一个字符,为该字符登记下标
            ed++;
            if(Max<ed-st) Max = ed-st;
        }
        printf("%d\n", Max);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值