[蓝桥杯]校内培训交流会总结

//【蓝桥杯培训交流会 整理]
//破解编程题目
/* 破解编程题目
    编程题是如何评测的?
        黑箱测试:
        程序-> 编译->运行->比对
        运行:
            1. 将stdin重定向data.in
            2. 将stdout重定向到user.out
        比对:
            比对data.out和user.out是否相同

    出题人如何准备数据?
        出题人会预先设定一组数据:
            data0.in - data0.out
            data1.in - data1.out
            data2.in - data2.out    
        出题人目的:
            测试程序正确性和效率
            随机数据
            边界数据
            特殊数据(某些算法会退化)
            规模最大的数据
            较小规模数据(给部分分)

    我们如何应对?                
        根据数据规模和时间限制估计复杂度
        (现在主流电脑1秒 = 10^8复杂度量级)
            放弃满分,争取部分分

        一些常见的数据规模和出题人期望的复杂度
           N <= 12 O(N!)
           N <= 20  O(2N)
           N <= 100 O(N3)
           N <= 1000 ~ 10000 O(N2)
           N <= 100000 ~ 1000000 O(NlogN)
           N <= 1000000+  O(N)
        有些题目会说明有部分分的数据范围
        有些题目没有说明,需要自行判断

        自测:对拍
            1. sample只是用来举例说明格式和题意,
            实际系统测试使用的数据比sample复杂得多。
            2. 把自己当出题人,字母测出来选手程序的BUG低效
            3. 出题人的目的:测试程序正确性和效率
        
        用程序生成随机数据生成
            举例:随机生成N个0~M的整数
        用程序生成特殊大数据
        [例]
        #include<iostream>
        #include<algorithm>
        using namespace std ;
        int n,m,seed ;
        int main()
        {
            cin >> n >> m >> seed ;
            srand(seed)
            for(int i = 0; i < n; i++)
            {
                int x = rand() % m ;
                cout << x << endl ;
            }
            return 0 ;
        }

        “对拍”
        很多时候一道题目都有很简单的朴素算法,但是效率很低
        我们要拿满分不得不实现一个更复杂的高效算法
        为了避免程序有BUG,我们可以再写一个朴素算法”对拍”
        举例:朴素匹配 VS KMP
        朴素算法很容易实现: if (s.find(p)) … 
        写一个脚本/程序,不断执行以下步骤
        生成一个随机小数据
        运行朴素匹配程序,得到结果文件1
        运行KMP程序,得到结果文件2
        比较文件1和文件2
        大数据朴素程序太慢,小数据也能测正确性
        对拍过程中,我们可以做别的题目
        需要一些Windows命令行和脚本的知识

        【大赛题型】
            结果填空题:
                1.已知测试数据,实现超长(~4小时)的编程大题。
                2.难度相对简单,告诉你怎么做
                3.程序已经有大致框架,可以测试
                4.“送分题”,丢分很可惜
                5.但是也有相当一部分没有满分

            程序填空题
                从main函数开始,自顶向下
                1.理解变量的作用
                2.推测每一条语句在做什么
                3.特别是每一个循环在做什么
                4.如果有递归,推测一个函数整体在做什么
                5.如果所填内容不影响程序流程,
                  不妨先随便填一个然后运行程序,了解流程
                
                如何答好程序填空题
                1.多读别人的程序
                2.出题人的程序相对比较精炼
                3.同样的逻辑,不同人可能有不同的写法
                4.见多识广,精益求精
                5.蓝桥杯历届题目在网上都能搜到别人公开的程序
                6.NOIP中有很多完善程序的题目,他山之石

            编程大题
        

    基础难度:
        基础:枚举、搜索、模拟
        考察基本功、基本的代码能力
        学习编程的第一道门槛:
        “我不会高大上的算法和数据结构,程序可能超时,但是不会出错”    
        编程基础最重要
        蓝桥杯中数列最多的题目类别
        
【枚举】
枚举的要点:
如果我一个算法根据数据范围、时限我知道要超时,我们怎么减少复杂度?
方法不外乎就是
1减少枚举的变量,原来我要枚举4个变量,能不能只枚举3个? 
2就是缩小枚举范围,我能不能通过改变枚举的变量,减少范围?
      确定需要的枚举变量
      确定枚举的范围(不重不漏)
1是二分,二分查找、二分搜索非常有效,
一般是复杂度从O(N)降到O(logN),使用范围也很广。
2是用Hash,空间换时间。
3此外还有一些常用的套路:
比如双指针,Leetcode上对应的分类是two pointer,
直译过来就是双指针,大概的思想就是滑动窗口。
4还比如前缀后缀和,也是空间换时间的思路。


【常见的枚举题类型】:
    枚举答案:
        答案难以计算,易于验证
        按某个顺序(从小到大)枚举每个可能的答案,验证。
        例题:哪天返回、猴子分香蕉、以及一些逻辑推理题

    枚举日期:
        例题:某年某月某日星期几
        关键:判断某个日期是不是合法
        int days[-1,31,28,31,30,31,30,31,31,30,31,30,31] ;
        is_leap_year(int year)
    
    枚举数位:(枚举有规律的数列)
        例题:等腰三角形
        12345678910111213…. 第N个数位是几
        1,2,2,3,3,3,4,4,4,4,……
        关键:
        分离各位数字,设计状态变量

    枚举位置、方向
        例题:字母阵列、回形填数
        关键:
        int dx = {-1, 1, 0, 0}; 
        int dy = {0, 0, -1, 1}  
        方向数组描述上下左右,设计状态变量
    
    基础的枚举类型:
        选择枚举变量:优化思想
        例题:第几个幸运数

    

二分、哈希、双指针、前缀、后缀和

STL-set用法:
set跟vector差不多,它跟vector的唯一区别就是,
set里面的元素是有序的且唯一的,只要你往set里添加元素,
它就会自动排序,而且,如果你添加的元素set里面本来就存在,
那么这次添加操作就不执行。

/*———————————————————————————————————————————————————————————
题目:平方十位数
问题描述: 
由0~9这10个数字不重复、不遗漏,可以组成很多10位数字。
这其中也有很多恰好是平方数(是某个数的平方)。
比如:1026753849,就是其中最小的一个平方数。
请你找出其中最大的一个平方数是多少?  
————————————————————————————————————————————————————————————*/
/*思路1:
枚举答案X [9876543210, 1026753849]
判断是不是恰好0-9十个数字
判断是不是完全平方数
令Y = int(sqrt(X))
判断Y * Y == X?*/ 

/*思路2:
枚举Y [100000, 30000]
计算X = Y * Y
判断X是不是恰好0-9十个数字 */
//常见问题:分离整数X的个位数字
if(X == 0) {
    //特判
}
while(X) {
  int d = X % 10;
  //处理d
  X /= 10;
}

bool valid(int x) { //恰好包含0-9
  if(x == 0) return false;
  set<int> s;
  while(x) {
    int d = x % 10;
    s.insert(d);
    x /= 10;
  }
  return s.size() == 10;
}
/*————————————————————————————————————————————————————————————*/

#include <iostream>
#include <set>
using namespace std;
//判断是不是包含0~9的代码
bool contains0_9(long long x)
{
    if(x==0)return false;//这里没什么用,算法的严谨性 
    set<long long> s;
    //分离x的各位数字,每次把x的末尾数字分离出去
    while(x)
    {
        int d=x%10;//d是x末位数字 
        s.insert(d);//把d插入s中 
        x/=10;//更新x 
    }
    return s.size()==10;
}

int main(int argc, char *argv[]) 
{
    //c语言中基本整型int占据两个字节,
    //取值范围-2^15--2^15-1(-32768--32767)肯定不够
    //用 long long 
    for (long long i=100000; i--; i>30000)
    {//找最大,倒着来 
        long long x=i*i;
        if(contains0_9(x))
        {
            cout << x << endl; 
            return 0; 
        }
    } 
    return 0;
}
//输出:结果为:9814072356。
        

/*———————————————————————————————————————————————————————————
题目:四平方和

问题描述:
四平方和定理,又称为拉格朗日定理:
每个正整数都可以表示为至多4个正整数的平方和。
如果把0包括进去,就正好可以表示为4个数的平方和。比如:
5 = 0^2 + 0^2 + 1^2 + 2^2
7 = 1^2 + 1^2 + 1^2 + 2^2
(^符号表示乘方的意思)
对于一个给定的正整数,可能存在多种平方和的表示法。
要求你对4个数排序:
0 <= a <= b <= c <= d
并对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法
程序输入为一个正整数N (N<5000000) 
//思路1:枚举abcd,判断a^2+b^2+c^2+d^2是否等于N
分析规模:
a:0 ~ sqrt(5000000 / 4)
b:0 ~ sqrt(5000000 / 3)
c:0 ~ sqrt(5000000 / 2)
d : 0 ~ sqrt(5000000)
abcd枚举的范围大约都是1000~2000
总枚举量 10^12 
还记得我们的经验吗? 1秒 = 10^8 

优化套路一:减少枚举变量,以计算代枚举
思路2:枚举abc,判断N-a^2-b^2-c^2是不是完全平方数
分析规模:
a:0 ~ sqrt(5000000 / 4)
b:0 ~ sqrt(5000000 / 3)
c:0 ~ sqrt(5000000 / 2)
总枚举量 10^9 还是会超时!

问题:能不能只枚举ab?
分析:只枚举ab,那么余下R=N-a^2-b^2
能否快速求出c^2+d^2=R的解?(或者快速判断出无解)
例如:N = 5, 当前枚举a=b=0。
c^2+d^2 = 5,能不能快速求出解c=1, d=2。

优化套路二:用哈希预处理出<c, d>的可行解
思路2:先预处理出c^2+d^2=R的可行解,保存在哈希表里。
枚举ab,通过查表判断无解或者有解。
————————————————————————————————————————————————————————————*/
/*
空间换时间的利器:哈希
查找一个东西存不存在是我们经常遇到的一个基本问题
哈希(hash):把将来需要查找的值预先保存起来,减少查找时间。
基本问题:给一个数组[1, 3, 13, 4, 5, 27 … ]
询问某个值V是否在数组里(在数组哪个位置)
遍历数组、枚举:O(N)
哈希:O(N)预处理,O(1)查询
实现方法:数组(int my_hash[值域])、set(TreeSet)、
map(TreeMap)、unordered_set(HashSet)、unordered_map(HashMap)

unordered_set可以把它想象成一个集合,它提供了几个函数让我们可以增删查:
unordered_set::insert
unordered_set::find
unordered_set::erase
由于它内部是用哈希表实现的,
所以上面这三个函数的平均时间复杂度都是O(1)。
我们可以看一个简单的例子。
#include<unordered_set>
#include<iostream>
using namespace std ;
int main()
{
    unordered_set<int> myset ;
    myset.insert(3) ;
    myset.insert(5) ;
    myset.insert(3) ;
    cout << myset.size() << endl ;
    myset.erase(3) ;
    if(myset.find(3) == myset.end())
        cout << "3 not found" endl ;
    return 0 ;
}
#include<unordered_set>头文件。然后有两点需要注意:
一是这个容器是个集合,所以重复插入相同的值是没有效果的。
二是find的返回值是一个迭代器(iterator),
如果找到了会返回指向目标元素的迭代器,没找到会返回end() */
//
#include<iostream>
int n;
unordered_map<int, int> f;
int main()
{
    cin >> n;
    for(int c = 0; c * c <= n / 2; c++) 
    {
        for(int d = c; c * c + d * d <= n; d++) 
        {
            if(f.find(c * c + d * d) == f.end()) 
            {
                f[c * c + d * d] = c;
            }
        }
    }
    for(int a = 0; a * a * 4 <= n; a++) 
    {
        for(int b = a; a * a + b * b <= n / 2; b++) 
        {
            if(f.find(n - a * a - b * b) != f.end()) 
            {
                int c = f[n - a * a - b * b];
                int d = int(sqrt(n - a * a - b * b - c * c) + 1e-3);
                cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
                return 0;
            }
        }
    }
    return 0;
}
//思路2
int n;
unordered_map<int, int> f;
int main()
{
    cin >> n;
    for(int c = 0; c * c <= n / 2; c++) 
    {
        for(int d = c; c * c + d * d <= n; d++) 
        {
            if(f.find(c * c + d * d) == f.end()) 
            {
                f[c * c + d * d] = c;
            }
        }
    }
    for(int a = 0; a * a * 4 <= n; a++) 
    {
        for(int b = a; a * a + b * b <= n / 2; b++) 
        {
            if(f.find(n - a * a - b * b) != f.end()) 
            {
                int c = f[n - a * a - b * b];
                int d = int(sqrt(n - a * a - b * b - c * c) + 1e-3);
                cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
                return 0;
            }
        }
    }
    return 0;
}

/*———————————————————————————————————————————————————————————
题目:2018年倍数问题

问题描述: 
众所周知,小葱同学擅长计算,
尤其擅长计算一个数是否是另外一个数的倍数。
但小葱只擅长两个数的情况,当有很多个数之后就会比较苦恼。
现在小葱给了你 n 个数,希望你从这 n 个数中找到三个数,
使得这三个数的和是 K 的倍数,且这个和最大。数据保证一定有解。

数据规模与约定 
对于 30% 的数据,n <= 100。
对于 60% 的数据,n <= 1000。
对于另外 20% 的数据,K <= 10。
对于 100% 的数据,1 <= n <= 10^5, 1 <= K <= 10^3,给定的 n 个数均不超过 10^8。

————————————————————————————————————————————————————————————*/
//思路:
//在n个数中找出3个数的和是k的倍数并且要最大,三重循环肯定是不可取的,所以用dfs。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
 
int n, k, ans = 0;
int temp[3], num[100005], vis[100005] = { 0 };
 
void dfs(int s)
{
    if (s == 3)
    {
        int t = temp[0] + temp[1] + temp[2];
        if (t%k == 0 && t > ans)
            ans = t;
        return;
    }
    else
    {
        for (int i = 0; i < n; i++)
        {
            if (!vis[i])
            {
                temp[s] = num[i];
                vis[i] = 1;
                dfs(s + 1);
                vis[i] = 0;
            }
        }
    }
}
 
int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; i++)
        scanf("%d", &num[i]);
    dfs(0);
    printf("%d\n", ans);
    return 0;
}

/*———————————————————————————————————————————————————————————
题目:分巧克力

问题描述: 
儿童节那天有K位小朋友到小明家做客。
小明拿出了珍藏的巧克力招待小朋友们。
小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。
为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。
切出的巧克力需要满足:
1. 形状是正方形,边长是整数  
2. 大小相同  
例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,
你能帮小Hi计算出最大的边长是多少么?
输入:
第一行包含两个整数N和K。(1 <= N, K <= 100000)  
以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000)  
————————————————————————————————————————————————————————————*/
//
#include<stdio.h>
#define N 100000
bool ok(int m);
int a,b;
int H[N],W[N];
int main()
{
    int c,l=1,max=0,r,ans=1;
    long long m;
    scanf("%d %d",&a,&b);
    for(c=0;c<a;c++)
    {
        scanf("%d %d",&W[c],&H[c]);
        if(max<=W[c])
        max=W[c];
        if(max<=H[c])
        max=H[c];  
    }
    r=max;
    while(l<=r)
    { 
        int m=(l+r)/2;
        if(ok(m))ans=m,l=m+1;
        else r=m-1;
    }
    printf("%d",ans);
    return 0;
}

bool ok(int m) 
{
    long long c = 0;
    for(int i = 0; i < a; i++) 
    {
        c+=(long long)(H[i]/m)*(W[i]/m);
    }
    if(c>=b) return true;
    return false;
}

/* 求部分和的利器:前缀和
对于一个数组A = [A1, A2, … AN],
我们将从Ai到Aj的和称作部分和,记为S[i, j]。
计算部分和是非常常见的问题
为了减少计算部分和的时间复杂度,我们往往使用前缀和:
s[i] = A1 + A2 + … Ai
例如A = [1, 2, 5, 4, 3]
则 S = [1, 3, 8, 12, 15]
我们要计算Ai + Ai+1 + … Aj,则需计算S[j] – S[i-1]。  
例如A2 + A3 + A4 = 2 + 5 + 4 = 11。
利用前缀和:S[4] – S[1] = 12 – 1 = 11
为了使用方便,我们一般令S[0] = 0,
这样A1+A2+A3 = S[3] – S[0] = 8 – 0 = 8. */

/*———————————————————————————————————————————————————————————
题目:K倍区间

问题描述: 
给定一个长度为N的数列,A1, A2, ... AN,
如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,
我们就称这个区间[i, j]是K倍区间。  
你能求出数列中总共有多少个K倍区间吗?
第一行包含两个整数N和K。(1 <= N, K <= 100000)  
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)

例子:
A = [1, 2, 3, 4, 5], K = 2
答案是6:[1, 2, 3], [1, 2, 3, 4], [2], 
[2, 3, 4, 5], [3, 4, 5], [4]
        
数据规模与约定 
思路:
基本思路:枚举起点Ai和终点Aj,计算部分和S = Ai + Ai+1 + Ai+2 + … Aj
如果S是K倍数,ans++
for i = 1 … N:
  for j = i … N:
    s = 0
    for t = i … j:
      s += A[t]
    if s % K == 0:
      ans++
————————————————————————————————————————————————————————————*/
//
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define ll long long
int N,K;
ll ans = 0;
int a[100000+5]; 
int S[100000+5]; //S[i]表示a[1]到a[i]的和
int cnt[100000+5]; //cnt[i]表示S模K为i的数的个数 
void solve()
{
    for(int i=1; i<=N; ++i)
    {
        S[i] = (S[i-1] + a[i])%K;
        ans += cnt[S[i]];
        cnt[S[i]]++;   
    } 
    cout<<ans+cnt[0]<<endl;
} 
int main()
{
    scanf("%d%d",&N,&K);
    memset(S,0,sizeof(S));
    memset(cnt,0,sizeof(cnt));
    int t = N;
    int i = 1; //从1开始 
    while(t--)
    {
        scanf("%d",&a[i]);
        i++;
    }
    solve();
    return 0; 
}

/*思路2:
思路2:枚举起点Ai和终点Aj,计算部分和SUM = s[j] – s[i-1]
如果SUM是K倍数,ans++
for i = 1 … N:
  for j = i … N:
    sum = s[j] – s[i-1]
if sum % K == 0:
      ans++                                                        组合计数:C(3, 2) + C(3, 2) = 3 + 3 = 6         
复杂度O(N^2),考虑到N=100000,还是会超时!

思路3:
计算前缀和s[0], s[1], s[2], … s[N]。
统计s[]中模K余0, 1, 2 … K-1的数量,
记为cnt[0], cnt[1], cnt[2] … cnt[K-1]。
答案就是:
cnt[0]*(cnt[0]-1)/2 + cnt[1]*(cnt[1]-1)/2 +… 
+cnt[K-1]*(cnt[K-1]-1)/2。
*/
map<int, int> cnt;
int main() {
  s[0] = 0;
  cnt[0] = 1;
  for(int i = 1; i <= n; i++) {
    s[i] = (s[i-1] + a[i]) % k;
    cnt[s[i]]++;
  }
  long long ans = 0;
  for(int i = 0; i < k; i++) {
    ans += (long long)(cnt[i]) * (cnt[i] - 1) / 2;
  }
  cout << ans << endl;
  return 0;
}    


/*———————————————————————————————————————————————————————————
题目:2018年日志统计

问题描述: 
小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,
日志共有N行。其中每一行的格式是:ts id  
表示在ts时刻编号id的帖子收到一个"赞"。  
现在小明想统计有哪些帖子曾经是"热帖"。
如果一个帖子曾在任意一个长度为D的时间段内收到不少于K个赞,
小明就认为这个帖子曾是"热帖"。具体来说,
如果存在某个时刻T满足该帖在[T, T+D)这段时间内
(注意是左闭右开区间)收到不少于K个赞,该帖就曾是"热帖"。  
给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。   
————————————————————————————————————————————————————————————*/
//
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n,d,k,ts,id;
vector<int> ve[1010];
int ans[1010];

int judge(int x)
{
    int len=ve[x].size();
    if(len<k)
    {
        return 0;
    }
    sort(ve[x].begin(),ve[x].end());
    int l=0,r=0,sum=0;
    while(l<=r &&r<len)
    {
        sum++;
        if(sum>=k)
        {
            //时间间隔小于d
            if(ve[x][r]-ve[x][l]<d){
                return 1;
            }
            else
            {
                l++;sum--;
            }
        }
        r++;
    }
    return 0;
}


int main()
{
    std::ios::sync_with_stdio(false);
    cin>>n>>d>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>ts>>id;
        ve[id].push_back(ts);
    }
    int cnt=0;
    for(int i=1;i<=1010;i++)
    {
        if(judge(i))
        {
            ans[++cnt]=i;
        }
    }
    for(int i=1;i<=cnt;i++)
    {
        cout<<ans[i]<<endl;
    }
    
    return 0;
}



/*———————————————————————————————————————————————————————————
题目:递增三元组

问题描述: 给定三个整数数组
A = [A1, A2, ... AN], 
B = [B1, B2, ... BN], 
C = [C1, C2, ... CN],
请你统计有多少个三元组(i, j, k) 满足:
1. 1 <= i, j, k <= N  
2. Ai < Bj < Ck 
  
【样例输入】
3
1 1 1
2 2 2
3 3 3

【样例输出】27 
        
数据规模与约定 
对于30%的数据,1 <= N <= 100  
对于60%的数据,1 <= N <= 1000 
对于100%的数据,1 <= N <= 100000 0 <= Ai, Bi, Ci <= 100000 
————————————————————————————————————————————————————————————*/
//
#include<stdio.h>
#include <string.h>
#define N 100004
long int n,anmin=0,cnmax=0,tn=0;
long int an[N],cn[N];
void swp(long int *t,long int x,long int y)
   {   long int tem=t[x];
      t[x]=t[y];
      t[y]=tem;
   }
long int  qp(long int t[N],long int r,long int l){
     
    long int x=t[r];
    long int i=r;
    long int j=l+1;
    while(1)
    {
        while(t[++i]<x&&i<l);
        while(t[--j]>x);
        if(i<j)swp(t,i,j);
        else break;
    }
    swp(t,r,j);
  return j;
}
void ppp(long int *t,long int r,long int l){
   long int m;
        if(r<l)
        {  m=qp(t,r,l);
         ppp(t,r,m-1);
         ppp(t,m+1,l);
        }
}
int main()
{long int i,j,sum=0;
long int a[N],b[N],c[N];
memset(an,0,sizeof(an));
memset(cn,0,sizeof(cn));
scanf("%ld",&n);
    for(i=1;i<=n;i++)
    { scanf("%ld",&a[i]);
     if(anmin>a[i]||i==1)anmin=a[i];
    }
      
    for(i=1;i<=n;i++)
    scanf("%ld",&b[i]);
    for(i=1;i<=n;i++){ 
           scanf("%ld",&c[i]);
         if(cnmax<c[i])cnmax=c[i];
      }
    ppp(a,1,n);
    ppp(c,1,n);
        for(i=1;i<=n;i++)
        {
            if(an[a[i]])an[a[i]]++;//相同 就加一即可
            else an[a[i]]=i;//通过排序后的位置得出 此数前面大的个数
            j=n-i+1;
           if(cn[c[j]])cn[c[j]]++;
           else cn[c[j]]=n+1-j;//通过排序后的位置得出 此数后面大的个数
        }
        for(i=1;i<=n;i++){   
              long int d=b[i]-1;
           long int d1=b[i]+1;
              while(an[d]==0&&d>=anmin)d--;
              while(cn[d1]==0&&d1<=cnmax)d1++;
              sum+=an[d]*cn[d1];
        }
        printf("%ld\n",sum);
        return 0;
}


/*———————————————————————————————————————————————————————————
题目:哪天返回

问题描述: 
小明被不明势力劫持。后莫名其妙被扔到x星站再无问津。
小明得知每天都有飞船飞往地球,但需要108元的船票,而他却身无分文。
他决定在x星战打工。好心的老板答应包食宿,第1天给他1元钱。
并且,以后的每一天都比前一天多2元钱,直到他有足够的钱买票。
请计算一下,小明在第几天就能凑够108元,返回地球。
————————————————————————————————————————————————————————————*/
//
#include<stdio.h> 
int main()
{
    int n=0,i,s=0,c=0;
    for(i=1; i<100; i+=2)
    {
        s=s+i;
        n++;
        if(s>=108)
        {
            printf("%d %d",s,n);
            break;
        }
    }
    return 0;
} 

/*———————————————————————————————————————————————————————————
题目:猴子分香蕉

问题描述: 
5只猴子是好朋友,在海边的椰子树上睡着了。
这期间,有商船把一大堆香蕉忘记在沙滩上离去。
第1只猴子醒来,把香蕉均分成5堆,还剩下1个,
就吃掉并把自己的一份藏起来继续睡觉。
第2只猴子醒来,重新把香蕉均分成5堆,还剩下2个,
就吃掉并把自己的一份藏起来继续睡觉。
第3只猴子醒来,重新把香蕉均分成5堆,还剩下3个,
就吃掉并把自己的一份藏起来继续睡觉。
第4只猴子醒来,重新把香蕉均分成5堆,还剩下4个,
就吃掉并把自己的一份藏起来继续睡觉。
第5只猴子醒来,重新把香蕉均分成5堆,哈哈,正好不剩!

请计算一开始最少有多少个香蕉。
需要提交的是一个整数,不要填写任何多余的内容
————————————————————————————————————————————————————————————*/
//
#include <stdio.h>
int main()
{
    int i=6;//要分5次所以从6开始计算
    while(true)
    {
        if(i%5==1)//第一次分会多出一个
        {
            int x1=i-(i-1)/5;//第一次均分后 都会少一份被拿走
            x1=x1-1;//对了,不能忘记每次多出来的香蕉也被醒过来的猴子偷吃了
            if(x1%5==2)
            {
                int x2=x1-(x1-2)/5;
                x2=x2-2;
                if(x2%5==3)
                {
                    int x3=x2-(x2-3)/5;
                    x3=x3-3;
                    if(x3%5==4)
                    {
                        int x4=x3-(x3-4)/5;
                        x4=x4-4;
                        if(x4%5==0 && x4!=0)//要确保到最后一个猴子醒过来的时候还有香蕉!且正好可以被均分
                        {
                            printf("%d \n",i);
                            break;
                        }
                    }
                }
            }
        }
        i++;
    }
    return 0;
}
//结果:3141


/*———————————————————————————————————————————————————————————
题目:第几个幸运数

问题描述: 

到x星球旅行的游客都被发给一个整数,作为游客编号。
x星的国王有个怪癖,他只喜欢数字3,5和7。
国王规定,游客的编号如果只含有因子:3,5,7,就可以获得一份奖品。
我们来看前10个幸运数字是:
3 5 7 9 15 21 25 27 35 45
因而第11个幸运数字是:49
小明领到了一个幸运数字 59084709587505,
他去领奖的时候,人家要求他准确地说出这是第几个幸运数字,否则领不到奖品。
请你帮小明计算一下,59084709587505是第几个幸运数字。
需要提交的是一个整数,请不要填写任何多余内容。
————————————————————————————————————————————————————————————*/
//
思路:用1作为初始值,先将1分别乘上3、5、7得到3、57,再将这三个数分别乘上3、5、7得到9、1521152535213549,以此类推。需要注意的是,为了得到59084709587505的正确序号,我们得保证前面依次乘出来的数要从小到大排序,并且不能重复,于是就要用的priority_queue和set。

代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
 
priority_queue<long long,vector<long long>,greater<long long> > q;
set<long long> s;
 
int main()
{
    int ans=-1;
    q.push(1);
    s.insert(1);
    while(1)
    {
        long long n=q.top();
        q.pop();
        ans++;
        if(n==59084709587505)
            break;
        for(int i=3;i<=7;i+=2)
        {
            long long t=n*i;
            if(!s.count(t))
            {
                q.push(t);
                s.insert(t);
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}


/*———————————————————————————————————————————————————————————
题目:小朋友崇拜圈

问题描述: 
班里N个小朋友,每个人都有自己最崇拜的一个小朋友(也可以是自己)。
在一个游戏中,需要小朋友坐一个圈,
每个小朋友都有自己最崇拜的小朋友在他的右手边。
求满足条件的圈最大多少人?
小朋友编号为1,2,3,...N
输入第一行,一个整数N(3<N<100000)
接下来一行N个整数,由空格分开。
要求输出一个整数,表示满足条件的最大圈的人数。

输入:
9
3 4 2 5 3 8 4 6 9
则程序应该输出:
4   
————————————————————————————————————————————————————————————*/
//


//【搜索】
/*
DFS:全排列、连通性
BFS:最短路、(状态转移)最少步数 */

/*———————————————————————————————————————————————————————————
题目:迷宫

问题描述: 
标题:迷宫

X星球的一处迷宫游乐场建在某个小山坡上。
它是由10x10相互连通的小房间组成的。

房间的地板上写着一个很大的字母。
我们假设玩家是面朝上坡的方向站立,则:
L表示走到左边的房间,
R表示走到右边的房间,
U表示走到上坡方向的房间,
D表示走到下坡方向的房间。

X星球的居民有点懒,不愿意费力思考。
他们更喜欢玩运气类的游戏。这个游戏也是如此!

开始的时候,直升机把100名玩家放入一个个小房间内。
玩家一定要按照地上的字母移动。

迷宫地图如下:
------------
UDDLUULRUL
UURLLLRRRU
RRUURLDLRD
RUDDDDUUUU
URUDLLRRUU
DURLRLDLRL
ULLURLLRDU
RDLULLRDDD
UUDDUDUDLL
ULRDLUURRR
------------
请你计算一下,最后,有多少玩家会走出迷宫? 
而不是在里边兜圈子。
请提交该整数,表示走出迷宫的玩家数目,不要填写任何多余的内容。
思路:深度优先遍历,并标记走过的路,如果返回走过的路说明一定走不出去。
————————————————————————————————————————————————————————————*/
//
#include<iostream>
#include<cstring>
using namespace std;
char map[10][10];
bool visited[10][10];
int Count = 0;
void DFS(int i, int j) 
{
    if (i<0 || i>9 || j<0 || j>9) 
    {//出界符合题意
        Count++;
        return;
    }
    if (visited[i][j]) 
    {
        return ;
    }
    visited[i][j] = true;//标记走过的路
    if (map[i][j] == 'U') 
    {
        
        DFS(--i, j);
 
    }
    else if (map[i][j] == 'D') 
    {
        DFS(++i, j);
    }
    else if (map[i][j] == 'L') 
    {
        DFS(i, --j);
    }
    else 
    {
        DFS(i, ++j);
    }
}

int main() 
{
    for (int i = 0; i<10; i++)
        for (int j = 0; j<10; j++) 
        {
            cin >> map[i][j];
        }
    for (int i = 0; i<10; i++) 
    {
        for (int j = 0; j<10; j++) 
        {
            memset(visited, false, sizeof(visited));
            DFS(i, j);
        }
    }
    cout << Count;
    return 0;
}


/*———————————————————————————————————————————————————————————
题目:凑算式

问题描述: 

     B      DEF
A + --- + ------- = 10
     C      GHI
        
这个算式中A~I代表1~9的数字,不同的字母代表不同的数字。

比如:
6+8/3+952/714 就是一种解法,
5+3/1+972/486 是另一种解法。

这个算式一共有多少种解法?
————————————————————————————————————————————————————————————*/
//
int a[9];    
bool used[10] = {false};    
int ans = 0;
void dfs(int x) 
{
    if(x == 9) 
    {
        if(a[2] == 0 || a[6] == 0 || a[3] == 0) return;
            if(fabs(a[0] + 1.0*a[1]/a[2] + 1.0*(a[3]*100+a[4]*10+a[5])/(a[6]*100+a[7]*10+a[8])-10) < 1e-6) 
                ans++;
            return;
    }
    for(int i = 1; i < 10; i++) if(!used[i]) 
    {
            a[x] = i;
            used[i] = true;
            dfs(x+1);
            used[i] = false;
    }
}

int main() 
{
    dfs(0);
    cout << ans << endl ;    
    return 0;
}


/*———————————————————————————————————————————————————————————
题目:搭积木

问题描述: 
小明最近喜欢搭数字积木,一共有10块积木,每个积木上有一个数字,0~9。
搭积木规则:每个积木放到其它两个积木的上面,
并且一定比下面的两个积木数字小。
最后搭成4层的金字塔形,必须用完所有的积木。
下面是两种合格的搭法:
   0
  1 2
 3 4 5
6 7 8 9

   0
  3 1
 7 5 2
9 8 6 4    
请你计算这样的搭法一共有多少种?
关键问题:怎么判断一定比下面的两个积木数字小
————————————————————————————————————————————————————————————*/
//思路一:Hardcode
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int ans = 0;
bool ok() 
{
    return a[0] < a[1] && a[0] < a[2] && a[1] < a[3] && a[1] < a[4] && a[2] < a[4] && a[2] < a[5]
           && a[3] < a[6] && a[3] < a[7] && a[4] < a[7] && a[4] < a[8] && a[5] < a[8] && a[5] < a[9];
}

int main()
{
    for(int i = 0; i < 10*9*8*7*6*5*4*3*2; i++) 
    {
        if(ok()) ans++;
        next_permutation(a, a+10);
    }
    cout << ans << endl;
    return 0;
}
//思路二:一维数组转二维矩阵
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int ans = 0;
bool ok() 
{
    int b[4][4];
    int k = 0;
    for(int i = 0; i < 4; i++)
        for(int j = 0; j <= i; j++)
            b[i][j] = a[k++];
        for(int i = 0; i < 3; i++)
            for(int j = 0; j <= i; j++) 
                if(b[i][j] >= b[i+1][j] || b[i][j] >= b[i+1][j+1]) return false;
        return true;
}

int main() 
{
    for(int i = 0; i < 10*9*8*7*6*5*4*3*2; i++) 
    {
        if(ok()) ans++;
        next_permutation(a, a+10) ;
    }
    cout << ans << endl ;
    return 0 ;
}


/*———————————————————————————————————————————————————————————
题目:方格填数

问题描述: 
如下的10个格子
   +--+--+--+
   |  |  |  |
+--+--+--+--+
|  |  |  |  |
+--+--+--+--+
|  |  |  |
+--+--+--+

入0~9的数字。要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)

一共有多少种可能的填数方案?

//思路分析
如何枚举?


给格子编号
   +--+--+--+
   | 0| 1| 2|
+--+--+--+--+
| 3| 4| 5| 6|
+--+--+--+--+
| 7| 8| 9|
+--+--+--+

如何处理相邻?Hardcode:

int adj[10][10] = {     {0, 1, 0, 1, 1, 1, 0, 0, 0, 0},
                       {1, 0, 1, 0, 1, 1, 1, 0, 0, 0},
                     {0, 1, 0, 0, 0, 1, 1, 0, 0, 0},
                     {1, 0, 0, 0, 1, 0, 0, 1, 1, 0},
                     {1, 1, 0, 1, 0, 1, 0, 1, 1, 1},
                     {1, 1, 1, 0, 1, 0, 1, 0, 1, 1},
                     {0, 1, 1, 0, 0, 1, 0, 0, 0, 1},
                     {0, 0, 0, 1, 1, 0, 0, 0, 1, 0},
                     {0, 0, 0, 1, 1, 1, 0, 1, 0, 1},
                     {0, 0, 0, 0, 1, 1, 1, 0, 1, 0}};
————————————————————————————————————————————————————————————*/
//
#include<iostream>
using namespace std ;
//给格子编号
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
//处理相邻
bool adj(int x, int y) 
{
    int rx = x / 4, cx = x % 4, ry = y / 4, cy = y % 4 ;
    return abs(rx - ry) <= 1 && abs(cx - cy) <= 1 ;
}


bool ok() 
{
    for(int i = 0; i < 9; i++) 
    {
        if(adj[a[i]][a[i+1]]) return false;
    }
    return true;
} 

int main()
{
    int ans = 0;
    for(int i = 0; i < 10*9*8*7*6*5*4*3*2*1; i++) 
    {
        if(ok()) ans++;
        next_permutation(a, a+10);
    }
    cout << ans << endl;
    return 0;
}

/*———————————————————————————————————————————————————————————
题目:寒假作业

问题描述: 

现在小学的数学题目也不是那么好玩的。
看看这个寒假作业:
   □ + □ = □
   □ - □ = □
   □ × □ = □
   □ ÷ □ = □
每个方块代表1~13中的某一个数字,但不能重复。比如:
 6  + 7 = 13
 9  - 8 = 1
 3  * 4 = 12
 10 / 2 = 5
以及: 
 7  + 6 = 13
 9  - 8 = 1
 3  * 4 = 12
 10 / 2 = 5
就算两种解法。(加法,乘法交换律后算不同的方案) 
你一共找到了多少种方案?注意÷需要能除尽
————————————————————————————————————————————————————————————*/
/*
思路:
   确定枚举的变量
   a[0] + a[1] = □
   a[2] – a[3] = □
   a[4] × a[5] = □
   a[6] ÷ a[7] = □?
*/
#include<iostream>
using namespace std ;
int a[8]; 
bool used[14] = {false}; 
int ans = 0 ;
void dfs(int x) 
{
    int y = 0 ;
    if(x == 8) 
    { 
        if(a[6] % a[7] != 0) return ;     
        y = a[6] / a[7] ;  
        if(y >= 1 && y <= 13 && !used[y]) ans++ ;     
        return ;
    }
    if(x == 2) y = a[0] + a[1] ; 
    if(x == 4) y = a[2] - a[3] ; 
    if(x == 6) y = a[4] * a[5] ; 
    if((y != 0) && (y < 1 || y > 13 || used[y])) return ;
    used[y] = true ;
    for(a[x] = 1; a[x] < 14; a[x]++) 
        if(!used[a[x]]) 
        {
            used[a[x]] = true ;    
            dfs(x+1);    
            used[a[x]] = false ;
        }
        used[y] = false ;
}

int main() 
{
    dfs(0) ;        
    cout << ans << endl ;        
    return 0 ;
}


/*———————————————————————————————————————————————————————————
题目:2018年全球变暖

问题描述: 
你有一张某海域NxN像素的照片,"."表示海洋、"#"表示陆地,如下所示:
.......
.##....
.##....
....##.
..####.
...###.
.......
其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。
例如上图就有2座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,
岛屿边缘一个像素的范围会被海水淹没。
具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。  
例如上图中的海域未来会变成如下样子:
.......
.......
.......
.......
....#..
.......
.......
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。 
————————————————————————————————————————————————————————————*/
//思路深搜并且标记统计原来的岛屿数目以及被淹没后
#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
using namespace std;
bool flag;//标记是否岛屿沉没
char a[1010][1010];//地图保存
int cnt=0;//统计周围#的数目
int n;//
int d[4][2]= {1,0,-1,0,0,1,0,-1};//枚举方向的数组
int ans=0;//被淹没后的岛屿个数
int res_ans=0;//没被淹没的岛屿的个数
void dfs(int x,int y)//回溯
{
    if(a[x][y]!='#')//如果这个点被标记成已经探索的岛屿内容或者是海洋,那么就返回
        return ;
    if(x>=n||x<0||y>=n||y<0)//如果越界了也是返回
        return ;
    if(flag==false)//如果还没确定不会被淹没,即当前是被淹没状态
    {
        cnt=0;//统计这个岛屿的当前点坐标x,y的上下左右的四个点是不是都是'#',如果确实是这样的,那么为我们就可以确定这个岛屿不会被淹没,我们姑且叫这样的点叫做不可淹没点,,那么只要一个岛屿有一个这样的点就一定不会被淹没
        for(int i=0; i<4; i++)//枚举四个方向
        {
            int tx=x+d[i][0];
            int ty=y+d[i][1];
            if(tx<n&&tx>=0&&ty<n&&ty>=0&&a[tx][ty]!='.')//假设枚举的四周的点不会超界并且不是海洋,那么cnt数目+1
            {
                cnt++;
            }
        }
        if(cnt==4)//如果四周的点都是这样的那么这个点就是不可淹没点
        {
            ans++;//最后不会被淹没的点+!
            flag=true;//这个岛屿标记已经确定,当前岛屿是不可以被淹没的,所以当回溯这个岛屿其他点的时候就不用再来检查了,因为一个岛屿有一个这样的不可淹没点就够了
        }
    }
    a[x][y]='*';//对于已经探索的岛屿的内容标记成'*',
    for(int i=0; i<4; i++)
    {
        int tx=x+d[i][0];
        int ty=y+d[i][1];
            dfs(tx,ty);//对于四个方向进行回溯岛屿的所有点
    }
}
int main()
{
    cin>>n;
    for(int i=0; i<n; i++)
    {
        scanf("%s",a[i]);
    }//输入数据
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<n; j++)
        {//遍历地图的所以坐标
            if(a[i][j]=='#')//如果被找到的是同一个岛屿那么会被标记成'*',不会进入回溯,只有是'#'才说明是未被搜索的岛屿的一角
            {
                res_ans++;//那么岛屿数目加1
                flag=false;//假设会淹没
                dfs(i,j);//回溯标记这个岛屿的所有点的坐标,并且如果不会被完全的淹没,那么就标记
            }
        }
    }
    cout<<res_ans-ans<<endl;//输出答案
}


/*———————————————————————————————————————————————————————————
题目:跳蚱蜢

问题描述: 
如图 所示: 
有9只盘子,排成1个圆圈。
其中8只盘子内装着8只蚱蜢,有一个是空盘。
我们把这些蚱蜢顺时针编号为 1~8
每只蚱蜢都可以跳到相邻的空盘中,
也可以再用点力,越过一个相邻的蚱蜢跳到空盘中。
请你计算一下,如果要使得蚱蜢们的队形改为按照逆时针排列,
并且保持空盘的位置不变(也就是1-8换位,2-7换位,...),至少要经过多少次跳跃?
————————————————————————————————————————————————————————————*/
//
int bfs() 
{
    string q[120000];
    int step[120000];
    int l, r;
    l = 0; r = 1;
    q[0] = "12345678";
    step[0] = 0;
    visited.insert(q[0]);
    while(l < r) 
    {
        string c = q[l];
        string n = c.substr(1, 7) + c[0];
        if(visited.find(n) == visited.end()) 
        {
            q[r] = n;
            step[r] = step[l] + 1;
            if(n == "87654321") return step[r];
            visited.insert(n);
            r++;
        }
         n = c.substr(2, 6) + c[1] + c[0];
        if(visited.find(n) == visited.end()) 
        {
            q[r] = n ;
            step[r] = step[l] + 1 ;
            if(n == "87654321") return step[r] ;
            visited.insert(n) ;
            r++ ;
        }
        n = c.substr(7, 1) + c.substr(0, 7) ;
        if(visited.find(n) == visited.end()) 
        {
            q[r] = n ;
            step[r] = step[l] + 1 ;
            if(n == "87654321") return step[r] ;
            visited.insert(n) ;
            r++ ;
        } 
        l++ ;
    }
    return -1;
}

int main()
{
    cout << bfs() << endl ;
    return 0 ;
}       

/*———————————————————————————————————————————————————————————
题目:青蛙跳杯子

问题描述: 
X星球的流行宠物是青蛙,一般有两种颜色:白色和黑色。
X星球的居民喜欢把它们放在一排茶杯里,这样可以观察它们跳来跳去。
如下图,有一排杯子,左边的一个是空着的,右边的杯子,每个里边有一只青蛙。
*WWWBBB其中,W字母表示白色青蛙,B表示黑色青蛙,*表示空杯子。
X星的青蛙很有些癖好,它们只做3个动作之一:
1. 跳到相邻的空杯子里。
2. 隔着1只其它的青蛙(随便什么颜色)跳到空杯子里。
3. 隔着2只其它的青蛙(随便什么颜色)跳到空杯子里。
对于上图的局面,只要1步,就可跳成下图局面:WWW*BBB

本题的任务就是已知初始局面,询问至少需要几步,才能跳成另一个目标局面。
输入为2行,2个串,表示初始局面和目标局面。
输出要求为一个整数,表示至少需要多少步的青蛙跳。  

输入:
*WWBB
WWBB*

则程序应该输出:2

再例如,输入:
WWW*BBB
BBB*WWW

则程序应该输出:10
我们约定,输入的串的长度不超过15
————————————————————————————————————————————————————————————*/
//
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f
#define mem(a,b)  memset( a,b,sizeof a)
string s1,s2;
map<string ,int > m;
struct node
{
    string s;
    int step;
    node(string ss,int tp)
    {    
        s=ss;
        step=tp;    
        }
};

queue<node> q;
int main()
{
    std::ios::sync_with_stdio(false);
    cin>>s1>>s2;
    int l=s1.size();
    q.push(node(s1,0));
    while(!q.empty())
    {
        node t=q.front();
        q.pop();
        if(t.s==s2)
            return cout<<t.step<<endl,0;
        for(int i=0;i<l;i++)
        {
            if(t.s[i]=='*')
                for(int j=max(0,i-3);j<=min(l-1,i+3);j++)
                {
                    string ts=t.s;
                    if(ts[j]!='*')
                    {
                        swap(ts[j],ts[i]);
                        if(m[ts]==0){
                            m[ts]=1;
                            q.push(node(ts,t.step+1));
                        }
                    }
                }
        }
    }
    return 0;
}


/*———————————————————————————————————————————————————————————
题目:穿越雷区

问题描述: 
X星的坦克战车很奇怪,它必须交替地穿越正能量辐射区和负能量辐射区
才能保持正常运转,否则将报废。某坦克需要从A区到B区去
(,B区本身是安全区,没有正能量或负能量特征),怎样走才能路径最短?
已知的地图是一个方阵,上面用字母标出了A,B区,
其它区都标了正号或负号分别表示正负能量辐射区。
例如:
A + - + -
- + - - +
- + + + -
+ - + - +
B + - + -
坦克车只能水平或垂直方向上移动到相邻的区。
  
状态:
(x, y)
最短:
step[x][y]

A 1 2 3 4
1 2 3 4 5
- + 4 5 6
+ 8 7 6 7
B 9 8 7 8
————————————————————————————————————————————————————————————*/
//
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
const int MAX_N = 101;
char s[MAX_N][MAX_N];
int book[MAX_N][MAX_N];
int N, startX, startY, endX, endY;
int MIN1 = 99999;
bool f;
int C = 0;
int next[4][2] = 
{
    {0, 1},
    {1, 0},
    {0, -1},
    {-1, 0}
};
int getX(char c) 
{
    for (int i = 0; i< N; i++) 
    {
        for (int j = 0; j < N; j++) 
        {
            if (s[i][j] == c) return i;
        }
    }
}
int getY(char c) 
{
    for (int i = 0; i< N; i++) 
    {
        for (int j = 0; j < N; j++) 
        {
            if (s[i][j] == c) return j;
        }
    }
}
void dfs(int x, int y, int step) 
{

    // 四种走法 上下左右 
    for (int i = 0; i < 4; i++) 
    {
        int tx = x + next[i][0];
        int ty = y + next[i][1];
        // 判断是否越界 
        if (tx < 0 || tx > N || ty < 0 || ty > N) continue;

        // 判断交替 
        if (s[x][y] == '+') f = true;
        else if (s[x][y] == '-') f =  false;

        if (tx == endX && ty == endY) 
        {
            // 更新最小值
            if (step < MIN1) MIN1 = step;
            return; //返回 
        }
        // 判断是否是障碍物或者已经走过 
        if (f && C > 0) 
        {
            if (book[tx][ty] == 0 && s[tx][ty] == '-') 
            {
                C++;
                book[tx][ty] = 1;
                dfs(tx, ty, step+1);
                book[tx][ty] = 0;
            }
        } 
        else if(!f && C > 0)
        {
            if (book[tx][ty] == 0 && s[tx][ty] == '+') 
            {
                C++;
                book[tx][ty] = 1;
                dfs(tx, ty, step+1);
                book[tx][ty] = 0;
            }
        } 
        else 
        {
            if (book[tx][ty] == 0) 
            {
                C++;
                book[tx][ty] = 1;
                dfs(tx, ty, step+1);
                book[tx][ty] = 0;
            }
        }
    }
}

int main() 
{
    freopen("in.txt", "r", stdin);
    scanf("%d", &N);
    getchar();
    for (int i = 0; i< N; i++) 
    {
        for (int j = 0; j < N; j++) 
        {
            scanf("%c", &s[i][j]);
            getchar();
        }
    }
    for (int i = 0; i < N; i++) 
    {
        for (int j = 0; j < N; j++) book[i][j] = 0;
    }
    startX = getX('A');
    startY = getY('A');
    endX = getX('B');
    endY = getY('B');
    book[startX][startY] = 1;
    dfs(startX, startY, 1);
    if (MIN1 == 99999) MIN1 = -1;
    cout << MIN1 << endl;
    return 0;
}



/*———————————————————————————————————————————————————————————
题目:乘积尾0

问题描述: 
如下的10行数据,每行有10个整数,请你求出它们的乘积的末尾有多少个零?
5650 4542 3554 473 946 4114 3871 9073 90 4329 
2758 7949 6113 5659 5245 7432 3051 4434 6704 3594 
9937 1173 6866 3397 4759 7557 3070 2287 1453 9899 
1486 5722 3135 1170 4014 5510 5120 729 2880 9019 
2049 698 4582 4346 4427 646 9742 7340 1230 7683 
5693 7015 6887 7381 4172 4341 2909 2027 7355 5649 
6701 6645 1671 5978 2704 9926 295 3125 3878 6785 
2066 4247 4800 1578 6652 4616 1113 6205 3264 2915 
3966 5291 2904 1285 2193 1428 2265 8730 9436 7074 
689 5510 8243 6114 337 4096 8199 7313 3685 211 

注意:需要提交的是一个整数,表示末尾零的个数。不要填写任何多余内容。
我们对每一个数进行因式分解,假设我们有n个数,
统计这n个数的质因子中一共有多少个2和5;
显然我们一个2和一个5能组成一个10,
我们要知道这些2和5能组成多少个10即可;
————————————————————————————————————————————————————————————*/
//
#include<stdio.h> 
 
int data[] ={5650,4542,3554,473,946,4114,3871,9073,90,4329,
2758,7949,6113,5659,5245,7432,3051,4434,6704,3594,
9937,1173,6866,3397,4759,7557,3070,2287,1453,9899,
1486,5722,3135,1170,4014,5510,5120,729,2880,9019,
2049,698,4582,4346,4427,646,9742,7340,1230,7683,
5693,7015,6887,7381,4172,4341,2909,2027,7355,5649,
6701,6645,1671,5978,2704,9926,295,3125,3878,6785,
2066,4247,4800,1578,6652,4616,1113,6205,3264,2915,
3966,5291,2904,1285,2193,1428,2265,8730,9436,7074,
689,5510,8243,6114,337,4096,8199,7313,3685,211};

struct node 
{
    int a2 , a5;    //用来储存每个数质因子中2和5的个数; 
};
node count(int num);  //计算num 质因子中2和5的数量,并存在结构体中 
int main()
{
    int len = sizeof(data) / sizeof(int);
    int a5 = 0,a2 = 0;
    for(int i = 0;i<len;i++)
    {
        a2 += count(data[i]).a2;
        a5 += count(data[i]).a5;
        
    }
    printf("%d\n",a2 < a5 ? a2 : a5);
    return 0;
} 

node count(int num)
{
    node temp;
    for(int i = 2; i *  i < num; i++)
    {
        int result = 0;
        while(num % i == 0)
        {
            result++;
            num /= i;
        }
        if(i == 2) temp.a2=result;
        else if(i == 5) temp.a5=result;
    }
    if(num!=1)
    {
        if(num == 2)
        temp.a2 = 1;
        else if(num == 5)
        temp.a5 = 1;
    }
    return temp;
}


//【模拟】
/*———————————————————————————————————————————————————————————
题目:小计算器

问题描述: 
模拟程序型计算器,依次输入指令,可能包含的指令有
1. 数字:'NUM X',X为一个只包含大写字母和数字的字符串,表示一个当前进制的数
2. 运算指令:'ADD','SUB','MUL','DIV','MOD',分别表示加减乘,除法取商,除法取余
3. 进制转换指令:'CHANGE K',将当前进制转换为K进制(2≤K≤36)
4. 输出指令:'EQUAL',以当前进制输出结果
5. 重置指令:'CLEAR',清除当前数字
————————————————————————————————————————————————————————————*/
//
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
typedef long long LL;
LL GOD;
int require,base;

LL transform(char *num)
{
    int i;
    LL sum=0,nowbase=1;
    for(i=strlen(num)-1; i>=0; i--)
    {
        if(isdigit(num[i]))
            sum+=(num[i]-'0')*nowbase;
        else
            sum+=(num[i]-'A'+10)*nowbase;
        nowbase*=base;
    }
    return sum;
}

int notransform(char *num)
{
    int sp=0;
    sscanf(num,"%d",&sp);
    return sp;
}

char* retransform(LL num)
{
    static char ans[100]={0};
    int index=1;
    if(!num)
        ans[index++]='0';
    while(num)
    {
        int temp=(num%base);
        if(temp<10)
            ans[index++]=temp+'0';
        else
            ans[index++]=temp-10+'A';
        num/=base;
    }
    ans[0]=index-1;
    return ans;
}

void runorder(char *str)
{
    char Torder[9][10]={"NUM","ADD","SUB","MUL","DIV","MOD","CHANGE","EQUAL","CLEAR"};
    char sp[10];
    sscanf(str,"%s",sp);
    char spnum[100]={0};
    if(strlen(sp)!=strlen(str))
        sscanf(str,"%*s%s",spnum);
    int i,j;
    char *temp=NULL;
    for(i=0;i<9;i++)
    {
        if(!strcmp(sp,Torder[i]))
        {
            switch(i)
            {
                case 0:
                    if(require==0)
                        GOD=transform(spnum);
                    else
                    {
                        if(require==1)
                            GOD+=transform(spnum);
                        if(require==2)
                            GOD-=transform(spnum);
                        if(require==3)    
                            GOD*=transform(spnum);
                        if(require==4)    
                            GOD/=transform(spnum);
                        if(require==5)    
                            GOD%=transform(spnum);
                        require=0;
                    }
                    break;
                case 1:
                    require=1;break;
                case 2:
                    require=2;break;
                case 3:
                    require=3;break;
                case 4:
                    require=4;break;
                case 5:
                    require=5;break;
                case 6:
                    base=notransform(spnum);break;
                case 7:    
                    temp=retransform(GOD);
                    for(j=temp[0];j>=1;j--)
                        putchar(temp[j]);
                    putchar('\n');
                    break;
                case 8:
                    GOD=0;
                    require=0;
            }
        }
    }
}

int main(int argc,char **argv)
{
    int cnt;
    scanf("%d",&cnt);
    base=10,require=0;
    while(cnt--)
    {
        char order[30];
        scanf("%*c%[^\n]",order);
        runorder(order);
    }
    return EXIT_SUCCESS;
}



/*———————————————————————————————————————————————————————————
题目:乘积最大

问题描述: 
今年是国际数学联盟确定的“2000——世界数学年”,
又恰逢我国著名数学家华罗庚先生诞辰90周年。
在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,
你的一个好朋友XZ也有幸得以参加。
活动中,主持人给所有参加活动的选手出了这样一道题目:
设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大。
同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:
有一个数字串:312, 当N=3,K=1时会有以下两种分法:
 3*12=36
 31*2=62
这时,符合题目要求的结果是:31*2=62
现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。
输入格式
  程序的输入共有两行:
  第一行共有2个自然数N,K(6≤N≤40,1≤K≤6)
  第二行是一个长度为N的数字串。
输出格式
  输出所求得的最大乘积(一个自然数)。
样例输入
4 2
1231
样例输出
62

解答:动态规划
dp[j][k] 代表前J位中插入了K个乘号能够达到的最大值
则状态转移方程为
dp[j][k]=max{dp[j][k],dp[i][k-1]*product(k+1,j)};
i是从k变到(j-1);

前J位插入K个乘号所能达到最大的值等于
(前i位插入了K-1的乘号与第i+1位到第J位表示的数(product(k+1,j))的乘积)的最大值。
————————————————————————————————————————————————————————————*/
//
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define max(x,y)  ((x)>(y)?(x):(y))
unsigned long long dp[41][31]={0};
char str[41];
int a[41];
unsigned long long product(int j,int k)//计算第j位到第K位组成的数 
{ 
 int i; 
 unsigned long long sum=0;
 for(i=j;i<=k;i++)
    sum=sum*10+a[i];
    return sum; 
}
int main()
{
      int n,K;
      scanf("%d%d",&n,&K);
      scanf("%s",str);
      int i,j,k;
      for(i=0;i<strlen(str);i++)
         a[i+1]=str[i]-'0';
    for(i=1;i<=strlen(str);i++)
       dp[i][0]=dp[i-1][0]*10+a[i];
    for(i=1;i<=K;i++)
        for(j=1;j<=n;j++)
        {
           unsigned long long temp=0;
           for(k=i;k<j;k++)
           temp=max(temp,dp[k][i-1]*product(k+1,j));
           dp[j][i]=temp;
        }
    printf("%lld\n",dp[n][K]);
    //system("pause");
    return 0;
}


/*———————————————————————————————————————————————————————————
题目:付账问题

【题目描述】  
几个人一起出去吃饭是常有的事。但在结帐的时候,常常会出现一些争执。
现在有 n 个人出去吃饭,他们总共消费了 S 元。其中第 i 个人带了 ai 元。幸运的是,所有人带的钱的总数是足够付账的,但现在问题来了:每个人分别要出多少钱呢?
为了公平起见,我们希望在总付钱量恰好为 S 的前提下,
最后每个人付的钱的标准差最小。这里我们约定,
每个人支付的钱数可以是任意非负实数,即可以不是1分钱的整数倍。
你需要输出最小的标准差是多少。

标准差的介绍:标准差是多个数与它们平均数差值的平方平均数,一般用于刻画这些数之间的“偏差有多大”。形式化地说,设第 i 个人付的钱为 bi 元,那么标准差为 : [参见p1.png]
  
【输入格式】  
从标准输入读入数据。
第一行包含两个整数 n、S;  
第二行包含 n 个非负整数 a1, …, an。
  
【输出格式】  
输出到标准输出。
输出最小的标准差,四舍五入保留 4 位小数。  
保证正确答案在加上或减去 10^−9 后不会导致四舍五入的结果发生变化。
  
【样例1输入】  
5 2333  
666 666 666 666 666
【样例输出】  
0.0000
  
【样例解释】  
每个人都出 2333/5 元,标准差为 0。
  
再比如:  
【样例输入】  
10 30  
2 1 4 7 4 8 3 6 4 7
  
【样例输出】  
0.7928
  
【数据说明】  
对于 10% 的数据,所有 ai 相等;  
对于 30% 的数据,所有非 0 的 ai 相等;  
对于 60% 的数据,n ≤ 1000;  
对于 80% 的数据,n ≤ 10^5;  
对于所有数据,n ≤ 5 × 10^5, 0 ≤ ai ≤ 10^9。

题解:
这是一道想出来就ac,想不出来就0分的贪心题,贪心策略如下:
先求一个不变的平均费用avg = S/n,如果有人付不起avg,
他们要把他们的钱全付完,不够的需要钱多于avg的人付,
但是这些付不起avg的人少付的钱会抬高>avg的人的付费平均值,
或许又会有人付不起这个新的avg,就是nowavg,
所以循环这个过程,直到没有人付不起被抬高的avg。
这样的话那些都能付得起新的avg的有钱人付的钱均相等,都是新的avg。
————————————————————————————————————————————————————————————*/
//
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define num ch-'0'
#define ld long double 
#define rep(i,a,b) for(register int i=(a);i<=(b);++i)
using namespace std;
const int N=500010;
ll cnt,n,m,a[N];
ld b[N],ans=0,avg,tot,nowavg;
inline void get(ll &res)
{
    char ch;bool flag=0;
    while(!isdigit(ch=getchar()))
        (ch=='-')&&(flag=true);
    for(res=num;isdigit(ch=getchar());res=res*10+num);
    (flag)&&(res=-res);
}

int main() 
{
    get(n),get(m);
    rep(i,1,n) get(a[i]);
    sort(a+1,a+n+1);
    nowavg=avg=1.0*m/n;
    tot=0;
    rep(i,1,n)
    {
        if(a[i]<nowavg)
        {
            ans+=(a[i]-avg)*(a[i]-avg);
            tot+=a[i];
        }
        else
        {
            nowavg=(m-tot)/(n-i+1);
            if(a[i]>=nowavg)
            {
                ans+=(nowavg-avg)*(nowavg-avg)*(n-i+1);
                break;
            }
            --i;
        }
    }
    ans/=(ld)n;
    ans=sqrt(ans);
    printf("%.4Lf",(ld)ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lx17746071609/p/10586239.html
今日推荐