51nod四级题(第一波汇总)

1051 最大子矩阵和()

思路:

用前缀和维护成块 然后 和 最大子序列和 同理。前缀和这块 O(n2)  后面最大子序列和 O(n),O(n3)。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 const int maxn = 500 + 5;
 5
 6 LL M[maxn][maxn], sum[maxn][maxn];// 第i行前j列的和
 7
 8 int main()
 9 {
10     int n, m;
11     scanf("%d%d", &m, &n);
12     memset(sum, 0LL, sizeof(sum));
13     for(int i = 1; i <= n; i++)
14     {
15         for(int j = 1; j <= m; j++)
16         {
17             scanf("%I64d", &M[i][j]);
18             sum[i][j] = sum[i][j - 1] + M[i][j];
19         }
20     }
21
22     LL Max = 0;//
23
24     for(int l = 1; l <= m; l ++)
25     {
26         Max = max(Max, sum[1][l]);
27         for(int r = l; r <= m; r++)
28         {
29             LL temp = 0;
30             for(int h = 1; h <= n; h++)
31             {
32                 temp += sum[h][r] - sum[h][l - 1];
33                 if(temp < 0)    temp = 0;
34                 else if(temp > Max) Max = temp;
35             }
36         }
37     }
38     cout << Max << endl;
39     return 0;
40 }

1060 最复杂的数(反素数)

思路:

  百度反素数,一切都明白了。

  http://blog.csdn.net/ACdreamers/article/details/25049767这篇博客讲的很详细。把这几个例题看懂,然后这题就很好解决了。

  !!!另外 你们可以尝试着不写maxcnt这个变量到dfs函数里头去,然后每次i从1取到64试试看,看看复杂度的差距。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 500 + 5;
 4 const int mod = 1000000007;
 5 typedef long long LL;
 6 typedef unsigned long long ull;
 7 const ull INF = ~0ULL;
 8
 9 //反素数的基础题
10 //求解1-n中约数数量最多的数字和它的约数数量。
11 ull ans, n;
12 LL ans_num;
13 int prim[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
14
15 void dfs(int deep, ull temp, LL num, int maxcnt)//选择到哪一个素数了  当前的数的大小,因子的数量 当前质因数最多的数量
16 {
17     if(deep >= 16)  return ;//素数用完了。
18     if(num == ans_num && temp < ans)
19     {
20         ans = temp;
21     }
22     if(num > ans_num)
23     {
24         ans_num = num, ans = temp;
25     }
26
27     for(int i = 1; i <= maxcnt; i++)
28     {
29         if(n / prim[deep] < temp)  break;
30         dfs(deep + 1, temp *= prim[deep], num * (i + 1), i);//剪枝, 其实是n < temp * prim[deep]
31
32     }
33 }
34
35 int main()
36 {
37     int T;
38     scanf("%d", &T);
39     while(T--)
40     {
41         cin >> n;
42         ans = INF, ans_num = 0;
43         dfs(0, 1, 1, 64);
44         cout << ans << " " << ans_num << endl;
45     }
46     return 0;
47 }

1070 Bash游戏 V4(斐波那契博弈)

思路:

  知道裴波那契博弈以后就很简单了。。有兴趣的看一下斐波那契博弈的证明。

  斐波那契博弈:有一堆个数为n的石子,游戏双方轮流取石子,满足:

    1)先手不能在第一次把所有的石子取完;

    2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。

  约定取走最后一个石子的人为赢家,求必败态。

  先手胜当且仅当n不是Fibonacci数。换句话说,必败态构成Fibonacci数列。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 const int maxn = 500 + 5;
 5 LL fib[100];
 6 int main()
 7 {
 8     fib[0] = 1, fib[1] = 2;
 9     for(int i = 2; i <= 50; i++)
10     {
11         fib[i] = fib[i - 1] + fib[i - 2];
12     }
13     int T;
14     scanf("%d", &T);
15     while(T--)
16     {
17         int n;
18         scanf("%d", &n);
19         int pos = lower_bound(fib, fib + 50, n) - fib;
20         if(fib[pos] == n)
21         {
22             puts("B");
23         }
24         else
25         {
26             puts("A");
27         }
28     }
29     return 0;
30 }

我把证明藏在下面,想看的自己点开。

 1 这个游戏叫做Fibonacci Nim,肯定和Fibonacci数列:f[n]:1,2,3,5,8,13,21,34,55,89,… 有密切的关系。如果试验一番之后,可以猜测:
 2
 3 就像“Wythoff博弈”需要“Beatty定理”来帮忙一样,这里需要借助“Zeckendorf定理”(齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和。
 4
 5 先看看FIB数列的必败证明:
 6
 7 1、当i=2时,先手只能取1颗,显然必败,结论成立。
 8
 9 2、假设当i<=k时,结论成立。
10
11      则当i=k+1时,f[i] = f[k]+f[k-1]。
12
13      则我们可以把这一堆石子看成两堆,简称k堆和k-1堆。
14
15     (一定可以看成两堆,因为假如先手第一次取的石子数大于或等于f[k-1],则后手可以直接取完f[k],因为f[k] < 2*f[k-1])
16
17      对于k-1堆,由假设可知,不论先手怎样取,后手总能取到最后一颗。下面我们分析一下后手最后取的石子数x的情况。
18
19      如果先手第一次取的石子数y>=f[k-1]/3,则这小堆所剩的石子数小于2y,即后手可以直接取完,此时x=f[k-1]-y,则x<=2/3*f[k-1]。
20
21      我们来比较一下2/3*f[k-1]与1/2*f[k]的大小。即4*f[k-1]与3*f[k]的大小,由数学归纳法不难得出,后者大。
22
23      所以我们得到,x<1/2*f[k]。
24
25      即后手取完k-1堆后,先手不能一下取完k堆,所以游戏规则没有改变,则由假设可知,对于k堆,后手仍能取到最后一颗,所以后手必胜。
26
27      即i=k+1时,结论依然成立。
28
29 对于不是FIB数,首先进行分解。
30
31 分解的时候,要取尽量大的Fibonacci数。
32
33 比如分解85:85在55和89之间,于是可以写成85=55+30,然后继续分解30,30在21和34之间,所以可以写成30=21+9,
34
35 依此类推,最后分解成85=55+21+8+1。
36
37 则我们可以把n写成  n = f[a1]+f[a2]+……+f[ap]。(a1>a2>……>ap)
38
39 我们令先手先取完f[ap],即最小的这一堆。由于各个f之间不连续,则a(p-1) > ap  + 1,则有f[a(p-1)] > 2*f[ap]。即后手只能取f[a(p-1)]这一堆,且不能一次取完。
40
41 此时后手相当于面临这个子游戏(只有f[a(p-1)]这一堆石子,且后手先取)的必败态,即先手一定可以取到这一堆的最后一颗石子。
42
43 同理可知,对于以后的每一堆,先手都可以取到这一堆的最后一颗石子,从而获得游戏的胜利。

1086 背包问题 V2(裸的多重背包)

思路:

  裸的东西。。百度“背包九讲”吧,01背包,完全背包,多重背包。然后这个是用二进制优化的?。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 100 + 5;
 4
 5
 6 int w[maxn], v[maxn], num[maxn];
 7 int f[50000 + 5];
 8 int n, V;
 9
10
11 void zero(int c,int val)
12 {
13     for(int i = V; i >= c; i--)
14         f[i] = max(f[i], f[i-c] + val);
15 }
16 void comple(int c,int val)
17 {
18     for(int i = c; i <= V; i++)
19         f[i] = max(f[i], f[i-c] + val);
20 }
21 void multi(int c,int w,int num)
22 {
23     if(c * num >= V)
24         comple(c ,w);
25     else
26     {
27         int k = 1;
28         while(k < num)//小于等于
29         {
30             zero(k * c, k * w);
31             num -= k;
32             k *= 2;
33         }
34         zero(num * c, num * w);
35     }
36 }
37
38 int main()
39 {
40     scanf("%d%d", &n, &V);
41     for(int i = 0; i < n; i++)
42     {
43         scanf("%d%d%d", &w[i], &v[i], &num[i]);
44     }
45     for(int i = 0; i < n; i++)
46     {
47         multi(w[i], v[i], num[i]);
48     }
49     cout << f[V] << endl;
50     return 0;
51 }

1103 N的倍数(抽屉原理)

思路:

  这题是spj,输出一种可行解就行了。抽屉原理嘛,就是sum = a[i]%n的加和,加和sum=0,说明从第一个数到当前这个数的和能被n整除,或者出现两个相同的sum,那么它们之间这段数字加和能被n整除。

  其实这题感觉自己写的是有点丑的。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 50000 + 5;
 4 int a[maxn];
 5 map<int, int>ma;
 6 int main()
 7 {
 8     int n;
 9     scanf("%d", &n);
10     for(int i = 0; i < n; i++)
11     {
12         scanf("%d", &a[i]);
13     }
14     int sum = 0, ok = 0, left = 0, right;
15     for(int i = 0; i < n; i++)
16     {
17         sum = (sum + a[i]) % n;
18
19         if(sum == 0)
20         {
21             ok = 1;
22             right = i;
23             break;
24         }
25         else if(ma.count(sum) == 0)
26         {
27             ma[sum] = i;
28         }
29         else//ma[sum]!=0
30         {
31             ok = 1;
32             left = ma[sum] + 1, right= i;
33             break;
34         }
35
36     }
37     if(ok)
38     {
39         printf("%d\n", right - left + 1);
40         for(int i = left; i <= right; i++)
41         {
42             printf("%d\n", a[i]);
43         }
44     }
45     else
46     {
47         puts("No Solution");
48     }
49     return 0;
50 }

1109 01组成的N的倍数(bfs)

思路:

  这题其实是一个bfs,就是你的最高位肯定是1吧,从1开始不断的后面加,每次加0或者加1,然后一小段一小段求%,而且很明显队列里最多只有n个元素,因为%n只有n种情况。详细见代码吧。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 1e6 + 5;
 4 int pre[maxn], p[maxn];
 5
 6 void show(int x)
 7 {
 8     if(x != -1)
 9     {
10         show(pre[x]);
11         printf("%d", p[x]);
12     }
13 }
14
15
16 int main()
17 {
18     int n;
19     scanf("%d", &n);
20
21     queue<int>que;
22     que.push(1);
23     pre[1] = -1;
24     p[1] = 1;
25     while(!que.empty())
26     {
27         int top = que.front();que.pop();
28
29         int p1 = (top * 10) % n;
30         int p2 = (top * 10 + 1) % n;
31
32         if(p1 == 0)
33         {
34             show(top);
35             puts("0");//因为p1是top后加上0求余得到的
36             break;
37         }
38         else if(p2 == 0)
39         {
40             show(top);
41             puts("1");//因为p2是top后加上1求余得到的
42             break;
43         }
44         if(pre[p1] == 0)
45         {
46             pre[p1] = top;
47             p[p1] = 0;//因为p1是top后加上0求余得到的
48             que.push(p1);
49         }
50         if(pre[p2] == 0)
51         {
52             pre[p2] = top;
53             p[p2] = 1;//因为p2是top后加上1求余得到的
54             que.push(p2);
55         }
56     }
57     puts("");
58
59     return 0;
60 }
61
62 //其实p数组是一个剪枝, 就是当同余的时候吧,如果这个数没出现过 那么更新pre,压进队,不然就不更新pre。因为同余的话,之前出现的数肯定更小。

先睡了orz

时间: 2024-10-05 13:54:43

51nod四级题(第一波汇总)的相关文章

1058 N的阶乘的长度(51NOD基础题)

1058 N的阶乘的长度(51NOD基础题) 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 输入N求N的阶乘的10进制表示的长度.例如6! = 720,长度为3. Input 输入N(1 <= N <= 10^6) Output 输出N的阶乘的长度 Input示例 6 Output示例 3 /* n! 的长度 len = (int)log10(n!) + 1 ; -->> len = log10(1) + log10(2) + .... + log1

1057 N的阶乘(51NOD基础题)

1057 N的阶乘(51NOD基础题) 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 输入N求N的阶乘的准确值. Input 输入N(1 <= N <= 10000) Output 输出N的阶乘 Input示例 5 Output示例 120 思路:由于最终结果比较大正常的 LL 肯定是不行的 , 所以仿照 10 进制数 构造出 10000 进制数 , 对万进制数的操作仿照十进制 #include <cstdio> #include <cstri

1012 最小公倍数LCM(51NOD基础题)

1012 最小公倍数LCM(51NOD基础题) 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 输入2个正整数A,B,求A与B的最小公倍数. Input 2个数A,B,中间用空格隔开.(1<= A,B <= 10^9) Output 输出A与B的最小公倍数. Input示例 30 105 Output示例 210 #include <cstdio> #define LL long long LL n , m ; LL result ; // 递归实现辗

1011 最大公约数GCD(51NOD基础题)

1011 最大公约数GCD(51NOD基础题) 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 输入2个正整数A,B,求A与B的最大公约数. Input 2个数A,B,中间用空格隔开.(1<= A,B <= 10^9) Output 输出A与B的最大公约数. Input示例 30 105 Output示例 15 /* <1> 循环实现 辗转相除法 <2> 递归实现 辗转相除法 */ #include <cstdio> #defi

1008 N的阶乘 mod P(51NOD基础题)

1008 N的阶乘 mod P(51NOD) 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 输入N和P(P为质数),求N! Mod P = ? (Mod 就是求模 %) 例如:n = 10, P = 11,10! = 3628800 3628800 % 11 = 10 Input 两个数N,P,中间用空格隔开.(N < 10000, P < 10^9) Output 输出N! mod P的结果. Input示例 10 11 Output示例 10 #includ

浙江省第6届程序设计竞赛结题报告汇总 zoj3202-3212

zoj 3202 Second-price Auction 水题,不解释了,直接贴代码 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; struct node{ int x; int y; }; struct node number[105]; int cmp(struct node a,struct node b){

牛客网刷题知识汇总2

构造函数不能声明为虚函数,析构函数可以声明为虚函数,且有时候必须声明为虚函数.构造函数和析构函数都不能调用虚函数. 先析构子类再析构父类,如果父类析构函数有虚函数,会导致调用子类的已经析构的内容. 先构造父亲类再构造子类,如果父类构造函数有虚函数,会导致调用子类还没构造的内容. A:构造函数不能声明为虚函数的原因是: 1 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的.而在构造一个对象时,由于对象还未构造成功.编译器无法知道对象 的实际类型,是该类本身,还是该

刷过的题 分类汇总

需要复习的算法题分类 二分搜索 ? 二叉树与分治part1:?    part2 ? 三种遍历 前序遍历 中序遍历 后续遍历 动态规划 ? 序列 dd dddd ddd linkedlist  ? array 中位数 median,quick sort subarray two pointers partition array 数据结构   part1 queue heap hashmap 并查集 trie树 扫描线 图搜索 回溯算法 part1  ? part2 ? 9. 各种排序 1. 基于

CCF CSP历年一二题代码汇总

实话说如果不是为了骗访问量,才不会写12题的qwq 201803-1 跳一跳 第一题向来是送分题...但是注意读题... 模拟题按题意走是坠稳的 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #define LL long long 6 #define debug(x) cout << "[" <&l