我菜得一批,所以开始恶补算法了。
:( 各位佬不要打我QWQ
具体看注解... python
# 再来一次哥还是不会啊啊啊啊 我吐了 我是不是不适合学?
# 继续写吧还有个杨辉三角...
# 由于是python所以要读一行的时候转成整型
n = int(input())
ans = []
for i in range(n):
temp = input().split()
temp = [int(x) for x in temp]
ans.append(temp)
# 初始化dp
# 注意如果你初始化的是另一个新的列表存值
# 那么状态转移的式子要这么写 dp[i][j]=dp[i-1][j]/dp[i-1][j-1] + ans[i][j] 要记得加上当前的值
dp = [[0 for y in range(n)]for x in range(n)]
# 一共要走N-1步
for i in range(1,n):# i 是行数 j是列数
# 判断状态转移
# 只能从左边转过来 从右边 从中间
for j in range(0,i+1):
if j==0:
dp[i][j] = dp[i-1][j] + ans[i][j]
if j==i:
dp[i][j] = dp[i-1][j-1] + ans[i][j]
if 0<j<i:# 夹在中间的选任意一条路,然后加上经过的这个点
dp[i][j] += max(dp[i-1][j],dp[i-1][j-1])+ans[i][j]
# print(dp)
# 由于条件限制 所以只能落在中间的区域
# 当层数是奇数时 会落在最后一层的中间
# 当层数是偶数时 会落在最后一层的中间左右 因为没有中间点...
if n%2==1:
print(dp[n-1][n//2]+ans[0][0])
else:
print(max(dp[n-1][n//2],dp[n-1][n//2-1])+ans[0][0])
C++
//万能头可能模拟不能用所以要自己写头文件了
#include<iostream>
#include<algorithm>
using namespace std;
int n;
int a[200][200];
int dp[200][200];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
cin>>a[i][j];
dp[1][1] = a[1][1];
//n-1决策
for(int i=2;i<=n;i++)
for(int j=1;j<=i;j++)
dp[i][j] = max(dp[i-1][j-1],dp[i-1][j])+a[i][j];
if(n%2==1)
{
cout<<dp[n][n/2+1];
}
else
{
cout<<max(dp[n][n/2+1],dp[n][n/2]);
}
return 0;
}
杨辉三角:
暴力版 python
# 参照杨辉三角的定义:
# 数字绘制形状如下:
# 1
# 1 1
# 1 2 1
# 1 3 3 1
# 1 4 6 4 1
# 1 5 10 10 5 1
# ....
# 杨辉三角的一行是组合数展开的系数... 但是我不知道?
# 第n行有n个数 除了第一行是1外,其他行的元素由上面一行的左右数字之和
# 前n行有(n+1)*n/2个数 1-n的累加和...
# 第n行第m个数表示为组合数C(m-1,n-1) 在n-1个数里选m-1个数有几种组合方式?
# 第n行之和 2^(n-1)
# 构造杨辉三角...可以使用加和的性质 就是一行元素等于它前一行的左右两个元素之和
# 直接用直角三角形算
MAXN = 1000
a = [[0 for i in range(MAXN)]for j in range(MAXN)]
a[1][1] = 1
for i in range(2,MAXN):
for j in range(1,i):
a[i][j] = a[i-1][j]+a[i-1][j-1]
# 直接输出就得到题目给的序列
# for i in range(2,MAXN):
# for j in range(1,i):
# print(a[i][j])
# target
x = int(input())
#直接遍历数
count = 0
for i in range(1,MAXN):
for j in range(1,i):
count+=1
if(x==a[i][j]):
print(count)
exit(0)
开始学一下推导
# 一开始不知道咋算的...
# 我的个人理解是这样的
# 主要思想是通过计算给出数字的位置求出在杨辉三角里面的具体位置 然后根据行数列数求前面的项数和
# 然后它发现通过斜着计算有规律 很快就能通过枚举组合数得到和数字一样的值
# 在斜行里面遍历...的时候采用二分枚举更快 所以这里又加上了二分
# 总的来说涉及到的必备要素有:
# 1. 推理出来斜行的公式 并且得到斜行的数字规律
# 从0行开始计算 0行就是全是1的... 从中间开始切断取左边
# 斜着是从左斜 对角线折中一半计算 因为优先算到左侧的数
# 第一个数 C(i,2i) 第二个C(i,2i+1) ...类推
# 1 1 --> C(0,0)=1 第0斜行第一个元素 在三角形里面是第1行第1列(个)
# 1 1 1
# 1 2 1 1 2 --> C(1,2)=2 第1斜行第一个元素 在三角形里面是第3行第2列(个)
# 1 3 3 1 ---> 1 3
# 1 4 6 4 1 1 4 6 --> C(2,4)=6 第2斜行第一个元素 在三角形里面是第5行第3列(个)
# 1 5 10 10 5 1 1 5 10
# 1 6 15 20 15 6 1 1 6 15 20--> C(3,6)=20 第3斜行第一个元素 在三角形里面是第7行第4列(个)
# 2. 二分查找要会写 使用二分查找查所有斜行的数字是否是输入数字
# 3. 计算出来要遍历的斜行范围... 16
# 求组合数 C(m,n)= n!/m!(n-m)! ——>实际上是 = ((n-m+1) * (n-m+2)...*n)/(1*2*3*...*(n-m))
def C(a,b):
res = 1
# 1.一种循环写法
# for i in range(a):
# res *= b/a
# a-=1
# b-=1
# 2.另一种循环写法 有点恶心 但是直观
for (i,j) in zip(range(1,a+1),range(b-a+1,b+1)):
res *= j/i
# print(i," ",j)
if res > target:#剪枝
return res
return res
def search(x, target):
# 判断是否在该斜行内
# 设置第一个元素作为起始 C(i,2i)
l = 2*x
r = target # 终点?也不知道怎么办 只能用这个作
if r<=l and C(x, l)!=target:
return False
while l<=r:
mid = l + (r-l)//2 # 中心点--如果起始点不是0就要加上l
temp = C(x, mid)
# 判断这个位置的是不是target
if temp > target:
r = mid-1
if temp < target:
l = mid+1
if temp==target:
# 计算现在的前几项
# 已知第n行有n个数 所以1+2+3+..+n-> (1+n)*n/2是 记录好的前n行的和 再加列数字
# C(x,mid)-> mid!/x!(mid-x)!-> 第mid+1行第x+1列的数字 不要搞混....
ans = (int(int((mid+1)*mid)//2))+x+1 # 这里注意 必须要是整数 用/会出现误差...
print(ans)
return True
return False
# print(C(2,4))
target = int(input())
for i in range(16,-1,-1):# 16-0斜行 包含全1斜行
# print(i)
if search(i, target):# 找到就停止循环
break
首先学会做初级版本的弹栈
python
# 初级版 无限制 给入栈出栈序列 判断是否出栈合理 Yes/No
zu = int(input())
for k in range(zu):
n = int(input())
# 入栈顺序
into = input().split()
into = [int(x) for x in into]
# 出栈顺序
outo = input().split()
outo = [int(x) for x in outo]
# 模拟栈
# 我有点憨憨 忘记最后一个元素就是栈顶元素...
# 弹出最后一个元素用pop()
# 记得每次初始化栈空
stack = []
j = 0
for i in range(n):
stack.append(into[i])
while stack !=[] and j<n and (stack[-1] == outo[j]):
j +=1
stack.pop()
if(stack == []):
print("YES\n")
else:
print("NO\n")
C++ 可以用STL 很爽...
#include<bits/stdc++.h>
using namespace std;
//给出一组数据 1 n[1,10000]
//给出几辆车 3
//给出入栈顺序 1 2 3
//给出出栈顺序 3 2 1
//验证出栈顺序是否可行 可行输出“Yes”否则输出“No”
const int maxn = 100001;
int out[maxn];//保存出栈顺序
stack<int> st;//栈用于保存元素
int main()
{
int zu, n, in[maxn];
cin>>zu;
while(zu--)
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>in[i];
}
for(int i=0;i<n;i++)
{
cin>>out[i];
}
int j=0;
while(!st.empty()) st.pop();
for(int i=0;i<n;i++)
{
//每次入栈都判断一下
st.push(in[i]);
while(!st.empty()&&st.top()==out[j]&&j<n)//这里注意一下不是out[i],是out[j]
{//因为我们控制的是j,控制弹出的下标,遍历的是i,弹出之后就接着下一个序列...
st.pop();
j++;
// cout<<"弹出!";
}
}
if(st.empty())
{
cout<<"Yes\n";
}
else
{
cout<<"No\n";
}
}
return 0;
}
//测试数据
//5
//3
//1 2 3
//3 2 1
//3
//1 2 3
//1 2 3
//3
//1 2 3
//2 1 3
//3
//1 2 3
//2 3 1
//3
//1 2 3
//3 1 2
//答案 Yes Yes Yes Yes No
python--法1 直接在不合理那里截断
m,n,k = map(int,input().split())
into = [int(x) for x in range(1,n+1)]
for p in range(k):
outo = input().split()
outo = [int(x) for x in outo]
stack = []
j = 0
for i in range(n):
# 法1
# 依旧是不能一个一个加,因为栈有容量了 并且有界-出栈序列
# 而且是需要继承上一次的结果的 不能从0再开始 所以这里不能用for i in range(n)
while(1):
if len(stack) == 0 or len(stack)<m and stack[-1]<outo[i]:
stack.append(into[j])
j+=1
# print(stack)
else:
break
if stack[-1] == outo[i]:
stack.pop()
else:
print("NO")
break
if stack == []:
print("YES")
python--法2 做标记
m,n,k = map(int,input().split())
into = [int(x) for x in range(1,n+1)]
for p in range(k):
outo = input().split()
outo = [int(x) for x in outo]
check = 0
j = 0
stack = []
for i in range(n):
# 法2
# 如果栈空 或者 栈顶元素小于 并且栈还有空位-> 进栈
# 栈满就直接跳到弹栈
while stack == [] or((stack[-1]<outo[i])and (len(stack)<m)) :
stack.append(into[j])
j+=1
# print(stack)
if stack[-1] == outo[i]:
stack.pop()
else:
check = 1
if check:
print("NO")
# print(stack)
else:
print("YES")
# print(stack)
C++
#include<iostream>
#include<stack>
using namespace std;
// 考试别用万能头 会变得很慢... 亲测慢
int m, n, k;
// 栈容量m
// 出栈序列长n
// 几次询问k
int main()
{
cin>>m>>n>>k;
stack<int> s;
int x;
for(int i=0;i<k;i++)
{
stack<int> s;
int p = 1;
int flag = true;
for(int j=0;j<n;j++)
{
cin>>x;
while(s.empty() || (s.top()< x && s.size()<m))
{
s.push(p);
p+=1;
}
if(s.top()==x) s.pop();
else flag = false;
}
if(flag==true) cout<<"YES\n";
else cout<<"NO\n";
}
return 0;
}
分考场:
python 80% 不知道是不是数据改了 老超时...
n = int(input())
m = int(input())
# 初始化关系表
# n*n 的 但是由于从1开始的下标 所以所有角标要+1
relation = [[0 for j in range(n+1)] for i in range(n+1)]
# 初始化 状态表
state = [[0 for j in range(n+1)] for i in range(n+1)]
for i in range(0,m):
# 记录 映射关系
u, v = map(int, input().split())
# 填入关系表格
relation[u][v] = 1
relation[v][u] = 1
# 有几个考场是算出来的 ->res 计算得到最小的考场数量
# 这里认识的考生不能在同一个考场里
# state[i][j] 第i个考场的第j个座位是哪个学生 ,相当于考场个数需要计算,但是座位不限,可以无限放人
res = 10000
def dfs(s,room):# 这里的room是我们计算过程中的考场数目,最后返回会赋值给全局变量res
global res
# 剪枝避免超时
if room >= res:
return
if s>n: # 考生人数不能超过总数
res = min(room, res)
return
# 这段我不会 所以要认真看...
# 遍历考场 因为最开始的时候room是0
for i in range(1,room+1):
# 看看考场能不能进去
sits = 0 # 设置当前位置 用于加入新学生
# 判断该考场里是否有人坐 判断是否是认识的人
# 如果有人坐并且不认识就下一个座位
while state[i][sits] and relation[s][state[i][sits]]== 0:
sits+=1
# 此时这个位置没人坐
if state[i][sits] == 0:
state[i][sits]=s
dfs(s+1,room)# 下一个人,因为考场座位不限
# 回溯
state[i][sits]=0
# 不属于以上的情况的 应该是出现了认识的人在同一考场
state[room+1][0]=s # 直接把人移动到下一个考场的第一个位置
dfs(s+1,room+1) # 下一位学生
state[room+1][0]=0# 回溯
# 深度搜索
dfs(1,0)
print(res)
后面两题是在某处看到的:
分糖:
给出一串长为n的序列,任意选值,选值规则是选了ai,不能选ai-1,ai-2,ai+1,ai+2,最后使得选值之和最大。
知道用动规,但是状态转移不会写,所以参考了一个佬的代码。
C++
#include<bits/stdc++.h>
#include<algorithm>
using namespace std;
// 给定一个n
//给定一串序列 类似 1 4 2 3 5 7 都大于0 1-10000
//任意选数使得和最大 并且选了一个数ai之后 ai+1,ai+2,ai-1,ai-2不能选
//动规
//但是状态转移怎么写??默认如果从a0开始选,那么a1,a2都不能被选,a3,a4,a5...可以选
//现在先不想第一步选什么,想想第二步可以由谁变来
//一开始选的时候默认先选最大的数,并且从头开始选能选到更多的数
//因为你从中间开始,一下子会死四个数(不能选四个)
//1.a0开始选 所以 dp[0] = a0
//2.a0-a1开始选 dp[1] = max(a1,a0)
//3.a0-a2开始选 dp[2] = max(a0,a1,a2)
//4.a0-a3开始选 dp[3] = max(a0,a1,a2,a3)...等等这好像没有价值了...因为可以变成
//dp[3] = max(dp[0]+a[3],dp[2]) ???
//因为选dp[0]的时候默认选a0,然后a1,a2不能选,只能选a3
//选dp[2]的时候可以选a0-a2里最大的,相当于包含了选a0-a2的工作... 它决定了你是选a0这条路,还是其他路...
//相当于当前这一步,可以由前一步转移而来(踩到不能选的数),或者前三步转移而来...因为i+1,i+2不能选...
//i[3,n) dp[i]= max(dp[i-1],dp[i-3]+a[i]) 这里想到了解释为什么i-1能选的原因...
//因为状态转移本就是从上一步转化而来,如果不能选上一步,那就转化成新的一步赋值
int main()
{
int n, temp;
cin>>n;
//数变大之后需要long long
vector<long long> a(n);
vector<long long> dp(n);
for(int i=0;i<n;i++)
{
cin>>a[i];
}
//初始化前三个
dp[0]=a[0];
dp[1]=max(a[0],a[1]);
//注意这里 max(a,b)只能比较两个,要写成特殊的格式并且类型要一致:max({a,b,c,d,...,})
dp[2]=max(dp[1],a[2]);
for(int i=3;i<n;i++)
{
dp[i]=max(dp[i-1],dp[i-3]+a[i]);
}
cout<<dp[n-1];
return 0;
}
//10
//1 5 2 3 4 3 2 1 6 2
python
# 给定一个序列 选值求最大和 不限次数选 值就是糖...
# 选值规则 选了当前的数ai ai-1 ai-2 ai+1 ai+2不能选
n = int(input())
a = input().split()
a = [int(x) for x in a]
dp = [0 for i in range(n)]
# print(dp)
# ai -> ai-1,ai-2,ai+1,ai+2不能选
# 最优策略是一开始选最大的值,减小大的值被不能选的概率
# 如果一开始从中间选,将会损失很多数
# 所以默认从第一位开始遍历
# 选到第4个数据的可能性是 选了第一个数
dp[0] = a[0]
dp[1] = max(a[1],a[0])
dp[2] = max(dp[1],a[2])
# dp[3] = max(dp[0]+a[3],dp[2])
for i in range(3,n):
dp[i] = max(dp[i-3]+a[i], dp[i-1])
print(max(dp))
春游巧克力:
给出一个长度为n的序列,代表巧克力边长,重量为ai*ai,
给出m次询问,每个询问带上一个包的容积,问每个包最多放几块巧克力。
分析知道sum(ai*ai)<=Vi,所以先排序巧克力重量,再求前几块的和,遍历。
C++
#include<bits/stdc++.h>
using namespace std;
// 给定一个n代表n块巧克力 给定一个m代表m次询问
// 输入一串序列a 代表巧克力的边长 1 4 2 3 4 重量为ai*ai
// 输入一串容量g 代表背包容量 1 2 4 9 1 3 9
// 输出每个背包最多能装多少块巧克力
int main()
{
int n, m;
cin>>n>>m;
vector<int> a(n);
vector<int> g(m);
for(int i=0;i<n;i++)
{
cin>>a[i];
a[i] = a[i]*a[i];
}
//排序...
//降序 sort(a.begin(),a.end(),greater<int>())
sort(a.begin(),a.end());
vector<long long> s(n+1);
// for(int i=0;i<n;i++)
// {
// cout<<a[i]<<" ";
// }
for(int i=0;i<n;i++)
{
s[i+1] = s[i]+a[i];
// cout<<s[i+1]<<endl;
}
int ans = 0;
vector<int> res(m);
for(int i=0;i<m;i++)
{
cin>>g[i];
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(s[j+1]<=g[i])
{
ans = j+1;
// cout<<"这是第"<<i<<"个包"<<endl;
// cout<<"可以放下"<<j+1<<"块巧克力"<<endl;
}
}
res[i] = ans;
cout<<res[i]<<" ";
}
return 0;
}
//5 5
//1 1 2 5 2
//2 1 4 10 5
python
## 给定一串序列 a [a1,a2,a3,...,an] 1<= n <=10000
## 给定一串序列 b [b1,b2,b3,...,bm] 1<= m <=10000
## 求 针对每一个bi,当ai*ai<=bi时,满足条件的ai有几个?
n, m = map(int,input().split())
a = input().split()
b = input().split()
a = [int(c)*int(c) for c in a]
b = [int(c) for c in b]
a.sort()
# print(a)
p = [0 for i in range(n)]
p[0] = a[0]
for i in range(1,n):
p[i] = p[i-1]+a[i]
# print(p)
res = []
for j in range(m):
count = 0
for i in range(n):
if p[i]<=b[j]:
count+=1
res.append(count)
result = ""
for i in range(m):
if i!=m-1:
result+=str(res[i])+" "
else:
result+=str(res[i])
print(result)
山形老贼什么时候写完《六花》第七卷,便是我成为百万富翁之时!