个人的变量命名习惯
- T test_case
- mark 标记
- n number
- e edge
- v vertex
- v vector
- v volume
- v value
- w weight
- d depth
- h head
- u/v/w 输入边的起点终点和权重
- vis visited
- l left
- r right
- mid middle
- pos position
- p pointer
- s/st start
- ed end
- m matrix
- g graph
- s set
- q queue
- c/cnt count
- ans answer
- ret return
- res result
- t/tmp temporary临时变量
数据分析
int的数据范围最高到1e9,超了记得换long long
第二步:构造 tm
结构体
tm t = {0, 0, 0, d, m - 1, y - 1900};
这里用到了 <ctime>
库里定义的结构体 tm
,这是 c/c++ 里专门用来表示日期的结构体。
结构如下:
struct tm { int tm_sec; // 秒 int tm_min; // 分 int tm_hour; // 时 int tm_mday; // 天 int tm_mon; // 月(0~11)❗️注意不是 1~12 int tm_year; // 年(从 1900 开始)❗️ };
所以:
t.tm_mday = d;
// 天 = 11t.tm_mon = m - 1;
// 月 = 4 - 1 = 3(代表 4 月)t.tm_year = y - 1900;
// 年 = 2025 - 1900 = 125
第三步:日期 +1 天
t.tm_mday += 1;
直接把天数加 1,可能会超出当前月的天数,比如加到 31 号或 29 号(2 月)等等。
第四步:让系统帮你“进位”
mktime(&t);
mktime
会自动处理你加了一天后产生的 月进位 / 年进位 / 闰年处理。
比如:
- 2月28日 +1 → 3月1日(非闰年)
- 2月28日 +1 → 2月29日(闰年)
- 12月31日 +1 → 下一年 1月1日
算法1-3暴力枚举
P2241统计方形
参考的大佬的题解:https://www.luogu.com.cn/problem/solution/P2241
注意要开long long,因为最坏的情况是从1➕到5000 * 5000,超出了int
等差数列求和公式:
S=n*(n+1) / 2
#include<bits/stdc++.h> using namespace std; int n,m; typedef long long ll; ll zheng,chang; int main() { cin >> n >> m; //对于正方形来说,子矩阵的个数是有原矩阵减去相同的数得到 for(int i = 0; i < n; i ++)//对于长方形来说,子矩形构成的矩阵的长宽是由原矩形长宽减去不同数而得 for(int j = 0; j < m;j++)//棋盘中的矩阵不是正方形就是长方形, { if(i == j) zheng += (n-i)*(m - i); else chang += (n-i)*(m-j); } cout << zheng << " " << chang << endl; return 0; }
P2089烤鸡
题目链接: https://www.luogu.com.cn/problem/P2089
一道比较简单的dfs,这里需要考虑的是最多有几种方案,因为题目说n最大5000,但是从题目意思可知,美味程度最大是30.数据量较小,如果非要说确定的话,3的10次方,最多开6w即可。
#include<bits/stdc++.h> using namespace std; int n,ans; //n表示需要的美味程度 int a[10000][10],cmp[10]; void dfs(int id,int degree){//id表示接下来考虑的是哪种配料,degree表示目前的美味程度 if(id > 10 || degree > n) return; if(id == 10 && degree == n){ for(int i = 0; i < 10;i++) a[ans][i] = cmp[i]; ans++; } for(int i = 1; i <= 3; i ++){ cmp[id] = i; dfs(id + 1,degree + i);//无需恢复现场,因为下次的值会覆盖 } } int main() { cin >> n; dfs(0,0); cout << ans << endl; if(ans){ for(int i = 0; i < ans ;i++) { for(int j = 0; j < 10; j ++) cout << a[i][j] << " "; cout << endl; } } return 0; }
P1618三连击
#include<bits/stdc++.h> using namespace std; int a,b,c; int used[10],m[10];//used用来标识数字是否用过,a用来存每一位上放的数字 bool ans = false; int seg(int w){//w能取1、2、3 int num = 0; for(int i = 3*w - 2;i <= 3*w;i++){ num *= 10; num += m[i]; //就是将对应的前三位,中三位,后三位取出来 } return num; } //一共9个数 void dfs(int id){//id表示接下来考虑的是第几位 if(id > 10) return; if(id == 10){ if(seg(1)*b == seg(2)*a && seg(1)*c == seg(3)*a){ ans = true; cout << seg(1) << " " << seg(2) << " " << seg(3) << endl; return; } } for(int i = 1; i <= 9;i++){ if(!used[i]){ m[id] = i; used[i] = 1; dfs(id+1); used[i] = 0; } } } int main() { cin >> a >> b >> c; dfs(1); if(!ans){ cout << "No!!!"; } return 0; }
P1036选数
该题的关键是选出数的组合不能重复,即位置不同也不行。
如1 、2、3和2、1、3是同一种
因此要设置一个参数来控制选数的顺序,每次选的时候只从他后面选,即可。
#include<bits/stdc++.h> using namespace std; const int N = 30; int q[N],ans,k,n; bool iszs(int x){ if(x == 0 || x == 1) return false; if(x == 2) return true; for(int i = 2; i < x;i++){ if(x % i == 0) return false; } return true; } void dfs(int id,int sum,int order){ if(id == k){ if(iszs(sum)) ans++; return; } for(int i = order;i < n; i ++) dfs(id+1, sum + q[i],i + 1); } int main() { cin >> n >> k; for(int i = 0; i < n;i++) cin >> q[i]; dfs(0,0,0); cout << ans << endl; return 0; }
P1088火星人
该题的意思就是从给定的一个全排列,顺序往下m个,然后输出他。那么在写的时候按全排列写即可,第一次直接定位到输入的全排列即可
#include<bits/stdc++.h> using namespace std; const int N = 10010; int n, m, ans, flag; //n表示总共几个数排列,m表示要加上的数,ans表示方案数 int a[N]; bool used[N]; void dfs(int u)//接下来考虑第几个数 { if(flag == 1) return ; if(u > n) { ans++; if(ans == m + 1) { for(int i = 1; i <= n; i ++) cout << a[i] << ' '; flag = 1; } return ; } for(int i = 1; i <= n; i ++) { if(ans == 0) i = a[u];//将输入的作为排列起点 if(!used[i]) { a[u] = i; used[i] = 1; dfs(u + 1); a[u] = 0; used[i] = 0; } } } int main() { cin >> n >> m; for(int i = 1; i <= n; i ++) cin >> a[i]; dfs(1); return 0; }
P3799小Y拼木棒
#include<bits/stdc++.h> #define int long long using namespace std; const int mod = 1e9 + 7,N = 1e5 + 10; int num[N]; int n,ans; signed main() { cin >> n; int Min = INT_MAX,Max = INT_MIN; for(int i = 0; i < n; i ++) { int a; cin >> a; num[a]++; Min = min(Min,a); Max = max(Max,a); } //先选两根相同的,再选出两根使这两根的长度之和与先前选出的相同 for(int i = Min + 1; i <= Max;i++){ if(num[i] >= 2){ for(int j = Min; j <= i / 2; j ++){ if(j != i - j) ans += num[i] * (num[i] - 1) *num[j] * num[i - j] / 2 % mod;//只有之前的两根相同,用公式cn2 else if(num[j] >= 2 && 2*j == i) ans += num[i] * (num[i] - 1) * num[j] * (num[j] - 1) / 4 % mod;//剩下的两根也想相同 } ans %= mod;//if里只能保证每次加的不超过mod,但是加完之后ans可能超过,因此要mod } } cout << ans << endl; }
P1044栈
#include<bits/stdc++.h> using namespace std; const int N = 20; int a[2*N][N]; int n; int dfs(int k,int num){//k表示当前是第几次操作,num表示当前栈内元素个数 int t = 0; if(k == 2*n){ if(num == 0) return 1;//是合法序列 else return 0;//不是合法序列 } if(a[k][num] != 0) return a[k][num];//如果之前计算过,直接返回 if(num < n) t += dfs(k + 1,num + 1); //栈未满可以push if(num > 0) t += dfs(k + 1,num - 1); //栈内有元素可pop a[k][num] += t;//记录当前状态的计算结果 return t; //当前k,num状态下,有多少种合法序列#include<bits/stdc++.h> using namespace std; const int N = 20; int a[2*N][N]; int n; int dfs(int k,int num){//k表示当前是第几次操作,num表示当前栈内元素个数 int t = 0; if(k == 2*n){ if(num == 0) return 1;//是合法序列 else return 0;//不是合法序列 } if(a[k][num] != 0) return a[k][num];//如果之前计算过,直接返回 if(num < n) t += dfs(k + 1,num + 1); //栈未满可以push if(num > 0) t += dfs(k + 1,num - 1); //栈内有元素可pop a[k][num] += t;//记录当前状态的计算结果 return t; //当前k,num状态下,有多少种合法序列 } int main() { cin >> n; cout << dfs(0,0); return 0; } } int main() { cin >> n; cout << dfs(0,0); return 0; }
写递归的要点
明白一个函数的作用并相信它能完成这个任务,千万不要跳进这个函数里面企图探究更多细节, 否则就会陷入无穷的细节无法自拔,人脑能压几个栈啊。
算法1-4递推与递归
P1464记忆化搜索
#include<bits/stdc++.h> using namespace std; #define int long long int d[25][25][25]; int w(int a,int b,int c){ if(a <= 0 || b <= 0 || c <= 0) return 1; if(a > 20 || b > 20 || c > 20) return w(20,20,20); if(a < b && b < c ) { if(!d[a][b][c-1]) d[a][b][c-1] = w(a,b,c-1); if(!d[a][b-1][c-1]) d[a][b-1][c-1] = w(a,b-1,c-1); if(!d[a][b-1][c]) d[a][b-1][c] = w(a,b-1,c); d[a][b][c] = d[a][b][c-1] + d[a][b-1][c-1] - d[a][b-1][c]; }else{ if(!d[a-1][b][c]) d[a-1][b][c] = w(a-1,b,c); if(!d[a-1][b-1][c]) d[a-1][b-1][c] = w(a-1,b-1,c); if(!d[a-1][b][c-1]) d[a-1][b][c-1] = w(a-1,b,c-1); if(!d[a-1][b-1][c-1]) d[a-1][b-1][c-1] = w(a-1,b-1,c-1); d[a][b][c] = d[a-1][b][c] + d[a-1][b-1][c] + d[a-1][b][c-1] - d[a-1][b-1][c-1]; } return d[a][b][c]; } signed main() { int a,b,c; while(scanf("%lld%lld%lld",&a,&b,&c)){ if(a == -1 && b == -1 && c == -1) break; int ans = w(a,b,c); printf("w(%lld, %lld, %lld) = %lld\n",a,b,c,ans); } return 0; }
P1928 外星密码
#include <bits/stdc++.h> using namespace std; string dg() { // 解压缩函数 int k;//压缩的次数 char ch;//输入的字符 string s = "", str = "";//s是最终答案,str是被压缩的字串 //cin >> ch 会跳过空格和换行符 while (cin.get(ch)) { //用cin.get()读取字符,避免跳过空格和换行 if (ch == '[') {//如果找到了被压缩的字串 cin >> k; // 读取压缩次数 str = dg(); // 递归调用 while (k--) { s += str;//把解压后的字串复制k次后添加到原来的字符串上 } } else if (ch == ']') {//如果找到了压缩的字串的末尾 return s; // 结束这一层递归并返回已经解压的字符串 } else { s += ch;//直接在最后添上这个字符。 } } return s; // 确保函数有返回值 } int main() { cout << dg(); return 0; }
P1255 数楼梯
#include<bits/stdc++.h> using namespace std; int n,len = 1,arr[5010][5010];//arr[k][i]第k阶台阶所对应的走法,len表示位数 void highadd(int k)//第k阶台阶,高精度加法 { for(int i = 1; i <= len; i ++) arr[k][i] = arr[k-1][i] + arr[k-2][i];//第k阶的方法等于一次走一步+一次性走两步的 for(int i = 1; i <= len; i ++) { if(arr[k][i] >= 10) { arr[k][i+ 1] += arr[k][i] / 10;//进位 arr[k][i] %= 10; if(arr[k][len + 1] != 0) len++;//如果最高位有进位,那么位数➕➕ } } } int main() { cin >> n; arr[1][1] = 1, arr[2][1] = 2;//初始化 for(int i = 3; i <= n; i ++)//从3开始避免越界 highadd(i); for(int i = len; i >= 1; i --) cout << arr[n][i]; ////逆序输出 return 0; }
P2437 蜜蜂路线
和数楼梯思路一样,只不过要上的阶数是n-m。
P1164 小A点菜
暴力dfs版本(果然TLE了)
#include<bits/stdc++.h> using namespace std; int f[110];//存储菜的价格 int n,m,used[110],ans; void dfs(int id,int num){//id表示当前考虑的是第几个菜品,num表示当前的菜钱 if(id == n) { if(num == m) ans++; return; } dfs(id+1,num + f[id]);//直接dfs会超时 dfs(id+1,num); } int main() { cin >> n >> m; for(int i = 0; i < n; i ++) cin >> f[i]; dfs(0,0); cout << ans; return 0; }
动态规划正解
这是一道简单的动规题,定义f[i]j为用前i道菜用光j元钱的办法总数,其状态转移方程如下:
(1)if(j==第i道菜的价格)f[i]j=f[i-1]j+1;
(2)if(j>第i道菜的价格) f[i]j=f[i-1]j+f[i-1]j-第i道菜的价格;
(3)if(j<第i道菜的价格) f[i]j=f[i-1]j;
说的简单一些,这三个方程,每一个都是在吃与不吃之间抉择。若钱充足,办法总数就等于吃这道菜的办法数与不吃这道菜的办法数之和;若不充足,办法总数就只能承袭吃前i-1道菜的办法总数。依次递推,在最后,我们只要输出f[n]m的值即可。
#include<bits/stdc++.h> using namespace std; int f[110][10010];//f[i][j]表示前i道菜(包括第i道),花费j元的方案数 int a[110];//存储菜的单价 int main() { int n,m; cin >> n >> m; for(int i = 1; i <= n; i ++) cin >> a[i]; for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++){//每种的方案数等于前i-1种选与不选的方案数之和 if(j > a[i]) f[i][j] = f[i-1][j] + f[i-1][j - a[i]]; else if(j == a[i]) f[i][j] = f[i-1][j] + 1; else f[i][j] = f[i-1][j]; } cout << f[n][m]; return 0; }
P1990 覆盖墙壁
GN:铺满前 N 列墙,且第 N+1列有一个单元被覆盖的方案数,且不考虑第N+1列的格子是上还是下!!
F2=2,F2 = F1+F0+2*G0, F1 = 1, F0 = 1,所以G0 = 0,又因为G1 = F0 + G0,所以G1 = 1. F0 = 1即什么都不放也是一种方案。
FN表示铺满前N*2的面积的墙的方案数.
FN的转移方程就是:
FN=FN-1+FN-2+2*GN-2(别忘了前面讲过GN-2的情况有两种)
而GN的转移方程就是:GN=FN-1+GN-1。
初始化:F0=1,G0=0;F1=G1=1;
但是,L形的瓷砖又怎么办呢?
(呵呵,刚开始想到这里的时候,我自己都蒙了。)
为了方便大家思考,我们先往简单的方向想。(以下是重点!!!)
我们可以用一个数组GN来表示**铺满前(N+1)*2的面积的墙,但是第(N+1)列有一个瓷砖已经被铺过(注意,是已经被铺过!)**的方案数。
所以,L形瓷砖的问题就已经被“初步”解决了。
所以,下面这种情况的方案数就是GN-2(因为实际上第N列已经铺满了,所以这里要处理的是前N-1列墙,所以多减了1)(如下图所示):
#include<bits/stdc++.h> using namespace std; const int mod = 10000, N = 1e6 + 10; int f[N],g[N]; int main() { int n; cin >> n; f[0] = 1; f[1] = g[1] = 1; for(int i = 2; i <= n; i ++){ f[i] = (f[i-1] + f[i-2] + 2*g[i-2]) % mod; g[i] = (g[i-1] + f[i-1]) % mod; } cout << f[n]; return 0; }
P3612 Secret cow code S
先看样例
cOW*−>cOW WcO−>*cOWWcO OcOWWc
我们把这三个字符串编号为1,2,3
现在我们要求第8位,假如我们已经知道在3串,能否逆推出在第2串中的位置呢?如果能,似乎问题就迎刃而解了,因为2逆推到1也是一个相同的子问题。
题目的古怪要求复制要先复制最后一个字符,再复制前缀,我们姑且先直接复制前缀.
cOW−>cOW cOW*−>*cOWcOW cOWcOW
现在求3串的8位置,3串长L,逆推回2串即为8−L/2位置
但我们复制的时候是先复制最后一个字符,所以要多减一为8−1−L/2
特别的,如果求的n刚好是先复制原串的最后一个位置,特殊处理
因为如果是原串的最后一个位置,假设原串长为L,则复制后的串为2L,若位置x - L / 2 - 1 == 0 即是原串的最后一个位置,则将他赋值为i
#include <bits/stdc++.h> using namespace std; string s; long long n,num,i; int main() { cin>>s>>n; num=s.length(); //求出原串的长度 while(num<n)//n表示要求字符的位置 { i=num; while(n>i) i*=2;//求出当前刚好包括n位置的串长 i=i/2;//得到当前串的一半长 n-=(i+1); if(n==0) n=i;//即上一个串的最后一个位置 } cout<<s[n-1]; return 0; }
P1259 黑白棋子的移动
最左边的o*与空位交换 然后空位再和最右边连续**的最后**两个交换
但是注意当o*与空位交换之后,连续的白棋只剩三个的时候规律发生了变化,此时直接打表。
#include<bits/stdc++.h> using namespace std; const int N = 210; char a[N]; int n; string db[4] = {"ooo*o**--*", "o--*o**oo*", "o*o*o*--o*", "--o*o*o*o*"};//无规律的 void out() { for(int i = 1; i <= 2*n+2;i++) cout << a[i]; cout << endl; } void move(int start,int endi) { swap(a[start],a[endi]); swap(a[start+1],a[endi+1]); out(); } int main() { cin >> n; for(int i = 1; i <= n;i++) a[i] = 'o'; for(int i = n+1; i <= 2*n;i++) a[i] = '*'; a[2*n + 1] = a[2*n + 2] = '-'; out();//输出起始序列 int len = n;//需要移动的黑/白棋 while(true) { move(len,2*len + 1);//空位和o*交换 len--; if(len == 3) break; move(len + 1,2*len + 1); } string ss; for (int i = 0; i < n - 4; i++) ss += "o*"; for (int i = 0; i < 4; i++) cout << db[i] << ss << endl; return 0; }
P1010幂次方
#include<bits/stdc++.h> using namespace std; int n; void recur(int x)//分解x使其表示为2和2(0)的组合 { for(int i = 14; i >= 0;i--) { if(pow(2,i) <= x) { if(i == 1) cout << "2";//2(1)不用再往后分解了且2^1输出为2,单独出来 else if(i == 0) cout << "2(0)";//2(0)也不用再往后分解了,单独出来 else{//指数不是这两种情况则还得分解 cout <<"2("; recur(i); cout << ")"; } x -= pow(2,i); if(x != 0) cout << "+";//加号处理的最简单方法:若此x还没分解完,则后面还有项,所以输出一个+号 } } } int main() { cin >> n; recur(n); return 0; }
P1228 地毯填补问题
棋盘是如何划分的:
- 设当前棋盘的左上角坐标为
(a, b)
,边长为l
。 - 该棋盘被划分成四个
l/2 × l/2
的小棋盘:- 左上角子棋盘范围:
横坐标:
[a, a + l/2 - 1]
纵坐标:[b, b + l/2 - 1]
- 右上角子棋盘范围: 横坐标:
[a, a + l/2 - 1]
纵坐标:[b + l/2, b + l - 1]
- 左下角子棋盘范围: 横坐标:
[a + l/2, a + l - 1]
纵坐标:[b, b + l/2 - 1]
- 右下角子棋盘范围: 横坐标:
[a + l/2, a + l - 1]
纵坐标:[b + l/2, b + l - 1]
- 左上角子棋盘范围:
横坐标:
void dfs(ll x, ll y, ll a,ll b, ll l)//(x,y)是障碍点,(a,b)是当前棋盘的左上角坐标,l是棋盘边长
初看这个问题,似乎无从下手,于是我们可以先考虑最简单的情况,既n = 2时
0 0 0 1 这时,无论公主在哪个格子,我们都可以用一块毯子填满
继续考虑n = 4的情况
我们已经知道了解决2 * 2的格子中有一个障碍的情况如何解决,因此我们可以尝试构造这种情况
首先,显然可以将4 * 4的盘面划分成4个2 * 2的小盘面,其中一块已经存在一个障碍了
而我们只需在正中间的2 * 2方格中放入一块地毯,就可以使所有小盘面都有一个障碍
于是,n = 4的情况就解决了
我们可以将n = 4时的解法可以推广到一般情况,既当n = 2 k时,我们均可以将问题划分为4个n = 2 k – 1的子问题,然后分治解决即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll x,y; //x,y表示公主坐标 int k; //表示迷宫规格 void dfs(ll x, ll y, ll a,ll b, ll l)//(x,y)是障碍点,(a,b)是当前棋盘的左上角坐标,l是棋盘边长 { if(l == 1) return ;//棋盘的大小是1×1,无法再继续拆分 if(x - a + 1<= l / 2&& y - b + 1<= l / 2)//则在左上角 { printf("%lld %lld 1\n",a + l/2,b + l/2);//则需在中心放置1号地毯,这里坐标是毯子的拐角 dfs(x,y,a,b,l/2);//递归处理左上角 dfs(a + l/2-1 , b+ l/2 , a , b + l/2 ,l/2);//右上角 dfs(a + l/2 , b+ l/2 - 1, a + l/2 , b ,l/2);//左下角 dfs(a + l/2 , b+ l/2 , a + l/2 , b + l/2 ,l/2);//右下角 } else if(x - a + 1 <= l / 2&& y - b + 1 > l / 2)//在右上角 { printf("%lld %lld 2\n",a + l/2,b + l/2 - 1);//则需在中心放置2号地毯 dfs(a + l/2-1,b + l/2-1,a,b,l/2);//递归处理左上角 dfs(x,y,a,b+l/2,l/2);//右上角 dfs(a + l/2,b+ l/2 - 1, a + l/2, b,l/2);//左下角 dfs(a + l/2, b+ l/2, a + l/2, b + l/2,l/2);//右下角 } else if(x - a + 1 > l / 2&& y - b + 1 <= l / 2)//左下角 { printf("%lld %lld 3\n",a + l/2 - 1,b + l/2);//则需在中心放置3号地毯 dfs(a + l/2-1,b + l/2-1,a,b,l/2);//递归处理左上角 dfs(a+l/2-1,b+l/2,a,b+l/2,l/2);//右上角 dfs(x,y,a+l/2,b,l/2);//左下角 dfs(a + l/2, b+ l/2, a + l/2, b + l/2,l/2);//右下角 } else { printf("%lld %lld 4\n",a + l/2 - 1,b + l/2-1);//则需在中心放置3号地毯 dfs(a + l/2-1,b + l/2-1,a,b,l/2);//递归处理左上角 dfs(a+l/2-1,b+l/2,a,b+l/2,l/2);//右上角 dfs(a+l/2,b+l/2-1,a+l/2,b,l/2);//左下角 dfs(x,y, a + l/2, b + l/2,l/2);//右下角 } } int main() { cin >> k >> x >> y; dfs(x,y,1,1,pow(2,k)); return 0; }
P1498 南蛮图腾
for(int j=i;j>0;j--)a[j]^=a[j-1];//修改数组
动态生成分形图案的每一行状态,它本质上是在模拟杨辉三角(Pascal's Triangle)的生成过程,但只关心每个位置的奇偶性(用异或运算实现)。
为什么倒序更新?
假设我们有一个数组 a = [1, 1, 0, 1]
,想要生成下一行:
- 正序更新(从左到右):会覆盖前面的值,导致后续计算错误。
- 倒序更新(从右到左):先处理高位,保留低位未修改的值,确保计算的正确性。
例如,生成杨辉三角第3行(索引从0开始):
- 原数组:
[1, 2, 1]
(但这里只关心奇偶性,实际存储的是[1, 0, 1]
) - 生成第4行时,需要从右向左更新,避免覆盖前一行数据。
#include<iostream> using namespace std; int n,a[1030]={1};//初始化数组,第一个元素为1,其余为0 int main(){ cin>>n; for(int i=0;i<1<<n;i++){//共2的n次方行 for(int j=1;j<(1<<n)-i;j++)cout<<" ";//前导空格,1-2^n-1,1-2^n-2... for(int j=i;j>0;j--)a[j]^=a[j-1];//修改数组 if(!(i%2))for(int j=0;j<=i;j++)cout<<(a[j]?"/\\":" ");//奇数行,2个空格,1个0等于2个空格 else for(int j=0;j<=i;j+=2)cout<<(a[j]?"/__\\":" ");//偶数行,4个空格 cout<<endl; } return 0; }
算法1-5贪心
总结
贪心的题一般会进行排序,并且多用结构体。一般是最小最大问题
P1223 排队接水
#include<bits/stdc++.h> using namespace std; int n; const int N = 1010; struct node{ double t; int id; bool operator<(const node&W)const{ return t < W.t; } }a[N]; int wait[N];//wait[i]表示第i人的等待时间 int main() { cin >> n; double ans = 0,time = 0;//time表示等待时间 for(int i = 1; i <= n; i ++){ cin >> a[i].t; a[i].id = i; } sort(a+1,a+1+n); for(int i = 1;i <= n; i ++) { cout << a[i].id << " "; time += a[i-1].t; wait[i] = time;//第i人的等待时间 ans += wait[i]; } cout << endl; printf("%.2f",ans*1.0 / n); return 0; }
P1803 凌乱的yyy / 线段覆盖
这道题贪心的思路是每次选择结束时间最早的,这样能为后面留下更多的时间参赛。
#include<bits/stdc++.h> using namespace std; const int N = 1e6 + 10; struct node{ int start,endi; bool operator<(const node&W)const{ return endi < W.endi; } }a[N]; int n; int main() { cin >> n; for(int i = 0; i < n;i ++) cin >> a[i].start >> a[i].endi; sort(a,a+n); int pre = a[0].endi;//第一个结束时间最短,一定会选上 int ans = 1;//记录方案数 for(int i = 1; i < n; i ++) { if(a[i].start >= pre) { ans++; pre = a[i].endi; } } cout << ans << endl; return 0; }
P1090合并果子
复杂度是 O(n^2)超时
sort复杂度是O(nlogn)
#include<bits/stdc++.h> using namespace std; int n;//果子的种类数 typedef long long ll; ll ans; int main() { cin >> n; vector<ll> a(n); for(int i = 0; i < n;i++) cin >> a[i]; ll tmp = 0; while(a.size() > 1){ sort(a.begin(),a.end());//集合排序使用迭代器,复杂度是O(nlogn) ll tmp = a[0] + a[1]; a.erase(a.begin()); a.erase(a.begin()); ans += tmp; a.push_back(tmp); } cout << ans << endl; return 0; }
O(n)做法
🍔 先说说:什么是堆?
- 堆(Heap)是一种特殊的完全二叉树,在编程里常用来做优先级排序。
- 有两种堆:
- 最大堆:顶端是最大的元素(默认的
priority_queue
) - 最小堆:顶端是最小的元素(我们需要的!)
- 最大堆:顶端是最大的元素(默认的
🧰 c++ 默认的 priority_queue
是最大堆
🧲 想要最小堆怎么办?
我们用这个写法:
priority_queue<int, vector<int>, greater<int>> q;
解释一下:
int
:存放的类型vector<int>
:底层容器greater<int>
:比较函数,告诉它“小的优先”,也就是最小堆!
🪄 你可以记住这个最小堆模板:
priority_queue<类型, vector<类型>, greater<类型>> 变量名;
📝 总结一下
操作 | 意义 | 举例 |
---|---|---|
push(x) | 把 x 放进堆里 | pq.push(5); |
pop() | 删除堆顶元素(最小值) | pq.pop(); |
top() | 查看堆顶元素(最小值) | cout << pq.top(); |
仅堆顶可读
贪心思路:每次选择最小的两堆进行合并
#include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { int n; cin >> n; priority_queue<ll, vector<ll>, greater<ll>> pq; // 小根堆 for (int i = 0; i < n; i++) { ll x; cin >> x; pq.push(x); } ll ans = 0; while (pq.size() > 1) { ll a = pq.top(); pq.pop(); ll b = pq.top(); pq.pop(); ll sum = a + b; ans += sum; pq.push(sum); } cout << ans << endl; return 0; }
P3817 小A的糖果
如果相邻两个盒子糖果的数量大于 x,就吃右边盒子的糖,否则不进行任何操作。
为什么要吃右边盒子的糖:这是因为如果我们吃掉左边盒子里的糖,就只会减少这一轮相邻两个盒子糖果的数量;如果我们吃掉右边盒子里的糖,那么这次操作还可以减少下一轮相邻两个盒子糖果的数量,符合贪心的逻辑。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5 + 10; ll a[N]; int main() { int n,x; cin >> n >> x; for(int i = 1; i <= n;i++) cin >> a[i]; ll ans = 0; for(int i = 1; i <= n;i++) //正好利用a[0] = 0 { if(a[i-1] + a[i] > x)//如果超了,则吃掉右边多余的糖果 { ans += a[i-1] + a[i] - x; a[i] = x - a[i-1]; } } cout << ans << endl; return 0; }
P1106 删数问题
sort函数的用法
默认是从小到大排序,如果要从大到小排序,则可写成如下格式:
sort(a,a+len,greater<int>());
重点是原左右次序
🧠 我们先说 string 的 erase 用法
string str = "abcdef"; str.erase(pos, len); // 从 pos的索引位置开始,删除 len 个字符
✅ 示例 3:只给一个参数,删除从这个位置到末尾
string str = "abcdef"; str.erase(3); // 删除从索引3开始(含)之后的所有字符 cout << str; // 输出 abc
🧠 vector 的 erase
用法也很类似
这里只给一个参数,只能删除给定位置索引的元素,不会删后面的
vector<int> v = {1, 2, 3, 4, 5}; v.erase(v.begin() + 2); // 删除索引为 2 的元素(也就是 3)
你也可以删除一个范围:(含头不含尾)
v.erase(v.begin() + 1, v.begin() + 4); // 删除 2~4(含头不含尾),结果是 {1, 5}
贪心的思想是每次删除数字中的极大值!
❓ 你想让一个数变小,怎么做?
从左到右,先比较高位!
- 最高位大 → 整体大
- 所以你想尽早删掉一个大数
- 如果你删的是左边的“高位的大数”,整体数就更小
所以: 从左往右找到第一个比后面大的数,删掉它,最有“贡献”!
#include<bits/stdc++.h> using namespace std; int main(){ string n; int s,i; cin>>n>>s; while(s){ for(i=0;n[i]<=n[i+1] && i + 1 < n.size();)//找极大值 i++; n.erase(i,1);//删除函数,就是从第i个位置连续删1个。如果不清楚删除函数,可以百度。 s--; } while(n[0]=='0'&&n.size()>1){//处理前导零,注意如果长度是1就不能再删了。 n.erase(0,1); } cout<<n; return 0; }
解法二:
#include<bits/stdc++.h> using namespace std; int a[260]; bool flag;//用来标识是否全为0 int main() { string n; int k; cin >> n >> k; for(int i = 1; i <= n.size();i++) a[i] = n[i-1] - '0'; int aim = n.size() - k,now = 0,tmp = 1,minp = 0;//tmp表示当前序列的起点 while(now < aim) { minp = tmp; for(int i = tmp;i <= k + tmp;i++) if(a[minp] > a[i]) minp = i;//到k+tmp之间有k个数足够删除 if(a[minp]) flag = true;//不为0的话 if(flag) cout << a[minp]; //首位特判,首位非0后面则输出 k -= minp - tmp;//表示删除了几个数 tmp = minp + 1;//下次从选了的数后面开始 now++;//当前选了的数加1 } if(!flag) cout << 0;//如果一直是0的话 return 0; }
P1478 陶陶摘苹果
贪心的思路是先取花费力气少的,留下更多的力气去拿后面的。
#include<bits/stdc++.h> using namespace std; struct node{ int x,y;//分别是高度和力气 bool operator<(const node&W)const{ return y < W.y; } }arr[5010]; int main() { int n,s; cin >> n >> s; int a,b; cin >> a >> b;//椅子高度和手伸直长度 if(n == 0){cout << 0; return 0;} for(int i = 0; i < n; i ++) cin >> arr[i].x >> arr[i].y; sort(arr,arr+n); int i = 0,sum = a + b,ans = 0; while(true) { if(arr[i].x <= sum && s >= arr[i].y){ ans++; s -= arr[i].y; } else if(s < arr[i].y || s < 0) break; i++; } cout << ans << endl; return 0; }
P5019 铺设道路
贪心的核心思想:
- 如果
a[i]
比a[i-1]
大:那么我们知道从a[i-1]
到a[i]
之间的差就是需要额外填充的部分,因此我们加上a[i] - a[i-1]
。 - 如果
a[i]
比a[i-1]
小:这个时候,我们不需要额外填充,只要关注前一个坑即可,因为当前坑已经被前一个坑的操作填补掉了。
为什么贪心是对的:
- 如果
a[i]
比a[i-1]
大,直接填充当前的差值a[i] - a[i-1]
,这相当于我们处理一个新坑的深度。 - 如果
a[i]
比a[i-1]
小,那么前一个坑已经处理过它并填充了这个部分,当前坑不需要额外的操作。
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int a[N],ans;//ans表示答案 int main() { int n; cin >> n; for(int i = 1; i <= n; i ++){ cin >> a[i]; ans += max(a[i] - a[i-1],0);//当前坑需要填的深度 } cout << ans << endl; return 0; }
P1208Mixing Milk
#include<bits/stdc++.h> using namespace std; const int N = 2e6 + 10; struct node{ int p,num; bool operator<(const node&W)const{ return p < W.p; } }a[N]; int main() { int n,m; cin >>n >> m; for(int i = 0; i < m;i ++) cin >> a[i].p >> a[i].num; int now = 0,ans = 0; int i = 0,need = n; sort(a,a+m); while(now != n) { int tmp = (a[i].num >= need ? need:a[i].num); // cout << "tmp=" << tmp << endl; ans += tmp * a[i].p; now += tmp; need -= tmp; i++; } cout << ans << endl; return 0; }
P1094 纪念品分组
我们先将数据进行排序,然后维护两个变量 x 和 y,让 x 指向开头,让 y 指向结尾。
一直循环,过程中会出现两种情况。
- 如果当前两个变量所指的两个数之和小于或等于 w,说明可行,就把它们两个分为一组,同时将 x 加 1,将 y 减 1,并将答案加 1,这是第一种情况。
- 如果当前两个变量所指的两个数之和大于 w,说明不可行,只将 y 减 1,同时答案加 1 即可,这是第二种情况。
重复以上过程,直到 x>y 时停止循环。
#include<bits/stdc++.h> using namespace std; const int N = 3e4 + 10; int a[N],used[N]; int ans; using namespace std; int main() { int w,n; cin >> w >> n; for(int i = 1; i <= n; i ++) cin >> a[i]; sort(a+1,a+n+1); int i = 1, j = n; while(i <= j) { if(a[i] + a[j] > w) j--; else i++,j--; ans++; } cout << ans << endl; return 0; }
P4995 跳跳!
记得开long long,因为hi最大可能为1e4,平方完1e8,继续加可能爆int
#include<bits/stdc++.h> using namespace std; const int N = 310; int a[N]; typedef long long ll; int main() { int n; cin >> n; for(int i = 1; i <= n; i ++) cin >> a[i]; sort(a,a+n+1); // for(int i = 0; i <= n;i ++) cout << a[i] << ' '; ll ans = 0,num = 0,i = 0,j = n; while(i <= j){ ans += pow(a[j] - a[i],2);//向右跳 i++,j--; } i = 1, j = n; while(i <= j) { ans += pow(a[j] - a[i],2);//向左跳 i++,j--; } cout << ans << endl; return 0; }
更便捷的写法:
#include<bits/stdc++.h> using namespace std; unsigned long long ans=0; int h[330]; bool sum=0; signed main() { int n; cin>>n; for (int i=1;i<=n;i++) cin >> h[i]; sort(h+1,h+n+1); int j=0,hpast=0; //j表示当前取的石头位置,hpast是上一次跳的石头高度 for (int i=1;i<=n;i++) { j=n-j+sum;// 交替:n-j 是从另一边开始,+sum 是让左右交替 sum=!sum; ans+=(h[j]-hpast)*(h[j]-hpast); hpast=h[j]; } cout<<ans; return 0; }
P4447 分组
在 c++ 中,map
是一个非常常用的 关联容器,定义在 <map>
头文件中。它是 STL(标准模板库) 的一部分,提供了 键值对(key-value) 的数据结构
🌟 简要定义
std::map<KeyType, ValueType>
KeyType
:键的类型(必须支持<
比较)ValueType
:值的类型
map
会 自动按照 key 排序,通常是按升序排列(默认使用 <
运算符)。
🔧 常用操作
操作 | 示例 | 说明 |
---|---|---|
插入 | m["key"] = value; 或 m.insert({key, value}); | 插入或修改元素 |
查找 | m.find(key) | 返回迭代器,指向元素;若不存在,返回 m.end() |
删除 | m.erase(key); | 删除指定 key 的元素 |
判断是否存在 | m.count(key) | 返回 0 或 1(map 不允许重复键) |
大小 | m.size() | 元素个数 |
清空 | m.clear() | 删除所有元素 |
✅ 正确写法回顾
std::map<int, int> m; auto i = m.begin(); // 第一种写法(解引用 + 点) (*i).second--; // 第二种写法(推荐,简洁) i->second--;
#include<bits/stdc++.h> using namespace std; map<int,int> m; int main() { int n, ans = INT_MAX; cin >> n; for(int i = 0; i < n; i ++) {int t;cin >> t;m[t] ++;} while (!m.empty()) { auto i = m.begin(), j = m.begin(); // 使用 auto推导迭代器类型 i->second--; // 已经画线,所以下面找递增是大于 int t = 1;// 若 i, j 所对应的能力值是连续的,且i对应的那一列高度不高于j,则继续画线 for (++j; j != m.end() && j->first == i->first + 1 && j->second > i->second; i++, j++) { t++; j->second--; } i = m.begin(); while (i != m.end() && i->second == 0) m.erase(i++->first); // 删除画完线高度为0的元素 if (t < ans) ans = t; //动态记录画线过程中的最小值 } cout << ans << endl; return 0; }
P1080 国王游戏
**ans1是最高位,**高位在前,低位在后
**p1是最高位,**高位在前,低位在后
sum1 是最低位,低位在前,高位在后
✅ 对比总结一下
数组 | 用途 | 低位 | 高位 |
---|---|---|---|
sum | 当前正在参与计算的数(参与乘法和除法) | sum[1] | sum[m] |
ans | 每次除法的结果 | ans[ls] | ans[1] |
p | 最终记录的最大结果 | p[lp] | p[1] |
所以这份代码内部其实用了两种顺序:
- 计算时(如乘法)用“低位在前”顺序(便于进位)
- 结果保存/比较/输出时用“高位在前”顺序(符合人类习惯)
#include <bits/stdc++.h> using namespace std; int n, a, b, m; // m 表示 sum 的有效位最大下标 int p[1010], lp = 0; // p 存当前最大值结果,高精度整数,lp 是最大下标 int sum[1010]; // sum 存当前乘积,高精度整数 int ans[1010], ls = 0; // ans 存除法结果临时数组,ls 是最大下标 int res; // 除法中保存的余数 struct node { int a, b; bool operator<(const node &W) const { return a * b < W.a * W.b; } } arr[1010]; bool compare() { int i = 0, j = 0; while (i <= lp && p[i] == 0) i++; // 去除 p 的前导 0 while (j <= ls && ans[j] == 0) j++; // 去除 ans 的前导 0 int len1 = lp - i + 1; int len2 = ls - j + 1; if (len1 > len2) return false; if (len1 < len2) return true; while (i <= lp && j <= ls) { if (p[i] < ans[j]) return true; if (p[i] > ans[j]) return false; i++; j++; } return false; } void cheng(int d) { for (int i = 0; i <= m; i++) sum[i] *= arr[d].a; for (int i = 0; i <= m; i++) { sum[i + 1] += sum[i] / 10000; sum[i] %= 10000; } if (sum[m + 1] != 0) m++; } void div(int d) { memset(ans, 0, sizeof(ans)); ls = 0; while (m >= 0 && sum[m] == 0) m--; // 去掉前导0 res = 0; int flag = 0; for (int i = m; i >= 0; i--) { res = res * 10000 + sum[i]; ans[++ls] = res / arr[d].b; if (ans[ls] == 0 && !flag) ls--; // 不保留前导 0 else flag = 1; res %= arr[d].b; } } int main() { cin >> n >> a >> b; for (int i = 0; i < n; i++) cin >> arr[i].a >> arr[i].b; sort(arr, arr + n); m = 0; memset(sum, 0, sizeof(sum)); sum[0] = a; for (int i = 0; i < n; i++) { div(i); // 先除 if (compare()) { lp = ls; memcpy(p, ans, sizeof(ans)); } cheng(i); // 再乘 } int i = 0; while (i <= lp && p[i] == 0) i++; // 去前导0 if (i > lp) { // 全是0 printf("0\n"); return 0; } printf("%d", p[i++]); for (; i <= lp; i++) { printf("%04d", p[i]); // 补足4位 } printf("\n"); return 0; }
算法1-7搜索
P1135 奇怪的电梯
1. memset(dist, 0x3f, sizeof(dist))
实际干了啥?
memset
会把内存中每个 字节(byte)都设成0x3f
(也就是十进制的63
)。- 而一个
int
在 c++ 中通常是 4 个字节(32 位)。
所以,每个 int
被填成:
0x3f3f3f3f
#include<bits/stdc++.h> using namespace std; const int N = 210; int k[N],dist[N]; int n,a,b; void dfs(int id,int step)//id表示当前在几楼,step表示到该楼的最小步数 { dist[id] = step; int nextid = id - k[id]; if(nextid >= 1 && step + 1 < dist[nextid]) dfs(nextid,step + 1);//下,注意剪枝 nextid = id + k[id]; if(nextid <= n && step + 1 < dist[nextid]) dfs(nextid,step + 1);//上 return; } int main() { memset(dist,0x3f,sizeof(dist)); cin >> n >> a >> b; for(int i = 1; i <= n; i ++) cin >> k[i]; dfs(a,0); if(dist[b] == 0x3f3f3f3f) cout << -1; else cout << dist[b]; return 0; }
P1219八皇后 checker challenge
#include<bits/stdc++.h> using namespace std; int a[20][20];//棋盘 int ans; int col[20],dg[40],udg[40]; int n; int path[20]; void dfs(int id)//当前在考虑第几行 { if(id == n + 1) { ans++; if(ans <= 3) { for(int i = 1; i <= n;i++) cout << path[i] << " "; cout << endl; } } for(int i = 1; i <= n; i ++) //考虑id行i列 { if(!col[i] && !dg[id + i] && !udg[i - id + n]) { col[i] = dg[id + i] = udg[i - id + n] = 1; path[id] = i; dfs(id + 1); col[i] = dg[id + i] = udg[i - id + n] = 0; } } } int main() { cin >> n; dfs(1); cout << ans; return 0; }
P1443 马的遍历
✅ 宽度优先搜索(BFS)特别适合用来找“无权图”的最短路!
🧠 为什么?
因为 BFS 是一层一层扩展的,它保证了:
第一次到达某个点的时候,所走的步数就是最短的。
#include<bits/stdc++.h> # define PII pair<int,int> using namespace std; queue<PII> q;//queue是先进先出 int f[410][410];//存到某点的最短步数 bool vis[410][410];//保存是否走过 int dx[8] = {-2,-1,1,2, 2, 1,-1,-2}; int dy[8] = { 1, 2,2,1,-1,-2,-2,-1}; int n,m,x,y; int main() { cin >> n >> m >> x >> y; memset(f,-1,sizeof(f)); memset(vis,false,sizeof(vis)); f[x][y] = 0; vis[x][y] = true; q.push({x,y}); while(!q.empty()) { int xi = q.front().first,yi = q.front().second; q.pop(); for(int i = 0; i < 8; i ++){ int u = xi + dx[i], v = yi + dy[i]; if(u < 1 || u > n || v < 1 || v > m || vis[u][v]) continue; vis[u][v] = true; q.push({u,v}); f[u][v] = f[xi][yi] + 1; } } for(int i = 1; i <= n;i++){ for(int j = 1; j <= m; j ++) cout << f[i][j] << " "; cout << endl; } return 0; }
dfs结果超时
#include<bits/stdc++.h> using namespace std; int path[410][410];//表示最短距离 int px[8] = {-2,-1,1,2, 2, 1,-1,-2}; int py[8] = { 1, 2,2,1,-1,-2,-2,-1}; int n,m,x,y; bool iscan(int x,int y) { if(x < 1 || x > n || y < 1 || y > m) return false; return true; } void dfs(int xi,int yi,int step)//表示当前所在的位置,以及到该点用了几步 { path[xi][yi] = step; for(int i = 0; i < 8;i++) { if(iscan(xi+px[i],yi+py[i]) && step + 1 < path[xi + px[i]][yi+py[i]]) dfs(xi + px[i],yi+py[i],step + 1); } return ; } int main() { memset(path,0x3f,sizeof(path)); cin >> n >> m >> x >> y; dfs(x,y,0); for(int i = 1; i <= n; i ++) { for(int j = 1; j <= m;j++) { if(path[i][j] == 0x3f3f3f3f) cout << -1 << " "; else cout << path[i][j] << " "; } cout << endl; } return 0; }
P2895 Meteor Shower S
✅ 那为什么 BFS 要判断“有没有走过”呢?
因为——
BFS 是一层一层地找最短路的,如果一个点你已经访问过,说明你之前已经用更短的时间走到它了!现在再来一次,就是浪费时间,还可能是“更慢的路径”。
#include<bits/stdc++.h> using namespace std; #define PII pair<int,int> int n; int a[310][310];//记录陨石砸落时间 int vis[310][310];//记录是否走过 int x,y,t;//陨石坐标,砸落时间 int dist[310][310]; //记录到达某点的最短时间 int dx[5]={0,0,0,1,-1};//方便移动和处理陨石砸落 int dy[5]={0,1,-1,0,0}; int ch(int x){//判断路过该点时是否陨石已经砸落,如果是没有陨石,相当于n年后砸落 if (x==-1) return 99999; else return x; } void sign(int i,int t)//标记陨石的下落时间 { if(x + dx[i] >= 0 && y + dy[i] >= 0 && (a[x + dx[i]][y + dy[i]] == -1 || a[x + dx[i]][y + dy[i]] > t)) a[x + dx[i]][y + dy[i]] = t; } int main() { cin >> n; memset(a,-1,sizeof(a));//陨石砸落时间初始化 for(int i = 1; i <= n; i++){ cin >> x >> y >> t; for(int i = 0; i < 5; i ++) sign(i,t); } queue<PII> q; vis[0][0] = true; q.push({0,0}); while(!q.empty()) { int x = q.front().first,y = q.front().second; q.pop(); int s = dist[x][y] + 1;//下一格子到达的时间等于当前格子加1 if(a[x][y] == -1){ cout << s - 1; return 0; } for(int i = 1; i <= 4; i ++) { int xi = x + dx[i],yi = y + dy[i]; if(xi >= 0 && yi >= 0 && s < ch(a[xi][yi]) && vis[xi][yi] == 0)//在边界内 //且下一时刻陨石没下楼且没被访问 { q.push({xi,yi}); vis[xi][yi] = 1; dist[xi][yi] = s; } } } cout << -1 << endl; return 0; }
算法2-1前缀和、差分与离散化
P8218 求区间和
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int a[N]; int main() { int n,m; cin >> n; for(int i = 1; i <= n; i ++) cin >> a[i],a[i] += a[i-1];//a[i]表示的是截止到i为止的和 cin >> m; for(int i = 0; i < m;i++) { int c,d; cin >> c >> d; cout << a[d] - a[c-1] << endl; } return 0; }
动态规划1
P2196 挖地雷
暴力DFS写法
#include<bits/stdc++.h> using namespace std; int a[30][30];//表示地窖之间的连接情况,1表示有连接 int path[30],ans;//存放最终结果的路径数组,最终的地雷数 int tempath[30],temp;//中间变量 int used[30];//是否用过 int passby;//表示走过的地窖数 int pass_ans;//表示最终走过的地窖数 int n;//表示地窖数 int num[30];//表示地窖内的地雷数组 bool isconnect(int id)//判断是否还有路径 { for(int i = 1;i <= n;i++) if(a[id][i] && !used[i]) return true; return false; } void dfs(int id)//当前准备进入的地窖是id号地窖 { if(!isconnect(id)) //没连接返回 { if(temp > ans)//找到了更多地雷的方案 { ans = temp; pass_ans = passby; for(int i = 1; i <= passby;i++) path[i] = tempath[i]; } return ; } for(int i = 1; i <= n;i++) { if(a[id][i] && !used[i]) { used[i] = 1;//该地窖走过了 passby++;//走过的地窖数➕➕ tempath[passby] = i; temp += num[i]; dfs(i);//去该地窖 used[i] = 0; tempath[passby] = 0; passby--; temp -= num[i]; } } } int main() { cin >> n; for(int i = 1; i <= n;i++) cin >> num[i]; for(int i = 1; i <= n - 1;i++)//共n-1行 { int index = i + 1,t; for(int j = 1; j <= n - i;j++) {cin >> t; a[i][index] = t; index++; } } for(int i = 1; i <= n;i++)//因为起点不一定是1,所以每个起点都遍历一下 { used[i] = 1; passby = 0; tempath[++passby] = i; temp += num[i]; dfs(i); temp -= num[i]; used[i] = 0; } for(int i = 1; i <= pass_ans;i++) cout << path[i] << " "; cout << endl << ans<< endl; return 0; }
Ac贪心:
memcpy
的正确用法是:
void *memcpy(void *dest, const void *src, size_t n);
也就是:
memcpy(目标地址, 源地址, 拷贝的字节数);
评论区
评论加载中...