算法笔记
nbuoj+洛谷基础算法题+其它
1224 哥德巴赫猜想(2)
题目描述
所谓哥德巴赫猜想,就是指任何一个大于2的偶数,都可以写成两个素数的和。现在输入一个偶数,要求寻找两个素数,使其和等于该偶数。由于可能有多组素数对满足条件,所以本题要求输出两数差最小的那两个素数。
注意 / 和% 的区别
以及函数调用求素数
#include<iostream>
using namespace std;
int issu(int a){
int res=1;
for(int i=2;i<a-1;i++){
if(a%i==0){
res=0;
break;
}
}
return res;
}
int main(){
int n;
cin>>n;
int h=n/2;
for(int i=h;i>1;i--){
if(issu(i)&&issu(n-i)){
cout<<i<<" "<<n-i<<endl;
return 0;
}
}
}
1171 多个数的最小公倍数
__gcd(a,b)//最小公约数
lcm(a, b) = a * b / gcd(a, b);//最大公倍数
题目描述
也许你已经会了求2个数字最小公倍数的方法,但是如果求多个数字的最小公倍数,你又能找到办法吗?
使用set标记break节省时间
#include<iostream>
using namespace std;
int main(){
int n;
int a[101];
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
}
for(int i=1;;i++){
int set=1;//是
for(int j=0;j<n;j++){
if(i%a[j]!=0){
set=0;//不是
break;
}
}
if(set==1){
cout<<i<<endl;
break;
}
}
}
1188 数字移位
题目描述
有n个整数,要求将前面各数字顺序向后移动m个位置,并将最后面的m个数变成最前面m个数。
#include<bits/stdc++.h>
using namespace std;
void reverse(int a[],int l,int r){
int temp=0;
int mid=(r-l+1)/2; //中间位置
for(int i=0;i<mid;i++){
temp=a[i+l];
a[i+l]=a[r-i];
a[r-i]=temp;
}
}
int main(){
int n=0,m=0;
int a[101];
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>a[i];
}
reverse(a,0,n-1);
reverse(a,0,m-1);
reverse(a,m,n-1);
for(int i=0;i<n-1;i++){
cout<<a[i]<<" ";
}
cout<<a[n-1]<<endl;
}
1195 巧妙推算走楼梯(动态规划)
题目描述
有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第M级,共有多少种走法?
后面数据以前面为基础进行、使用相同算法
#include<iostream>
using namespace std;
int a[45]={0};
int main(){
int n;
int m;
cin>>n;
a[0]=0;
a[1]=1;
a[2]=1;
a[3]=2;
for(int i=4;i<42;i++){
a[i]=a[i-1]+a[i-2];
}
for(int i=0;i<n;i++){
cin>>m;
cout<<a[m]<<endl;
}
}
P1090 合并果子(贪心)(STL栈)
#include<iostream>
#include<queue>
using namespace std;
priority_queue< int,vector<int>,greater<int> >q;
//注意格式 !空格 pop出最小的那个
int main(){
int n;
int a;
int sum=0;
cin>>n;
while(n--){
cin>>a;
q.push(a);
}
while(q.size()>=2){
int a=q.top();
q.pop();
int b=q.top();
q.pop();
q.push(a+b);
sum+=a+b;
}
cout<<sum;
}
自定义
#include<queue>
struct Node{
int x,y;
Node(int a=0, int b=0): x(a), y(b) {}
};
struct cmp{
bool operator()(Node a, Node b){
if(a.x == b.x) return a.y>b.y;
return a.x>b.x;
}
};
priority_queue<Node, vector<Node>, cmp>p;
P1219 八皇后(深搜+回溯)
注意for循环从1开始
n行n列
#include<iostream>
using namespace std;
int ans[14];//结果
int check[3][28]={0};//列,左右斜线
int sum=0;
int n;
void eq(int line){
if(line>n){//递归限制
sum++;
if(sum>3)return;//只要3个
else
{
for(int i=1;i<=n;i++)
cout<<ans[i]<<" ";//输出结果
cout<<endl;
return;
}
}
for(int i=1;i<=n;i++){//列遍历 有无 两斜线有无
if((!check[0][i])&&(!check[1][i+line])&&(!check[2][line-i+n])){
//规律
ans[line]=i;
check[0][i]=1;check[1][i+line]=1;check[2][line-i+n]=1;
eq(line+1);
//回溯搜索下一行
//复原
check[0][i]=0;check[1][i+line]=0;check[2][line-i+n]=0;
}
}
}
int main(){
cin>>n;
eq(1);//用行作为递归值
cout<<sum;
return 0;
}
P1443 马的遍历(搜索广度优先队列)
queue<A>q;
q.push(A{x1,y1,0});
#include<bits/stdc++.h>
#define re(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
struct A{
int x,y,cnt;
};
int mp[401][401];
bool vis[401][401];
//int xx[]={1,1,-1,-1,2,2,-2,-2};
//int yy[]={2,-2,2,-2,1,-1,-1,1};
int xx[]={1,2,-1,-2,1,2,-1,-2};
int yy[]={2,1,2,1,-2,-1,-2,-1};
int main(){
re(i,1,400)
re(j,1,400)
mp[i][j]=-1;
int x1,y1,m,n;
cin>>m>>n>>x1>>y1;
queue<A>q;
q.push(A{x1,y1,0});
vis[x1][y1]=1;//存入即遍历到
mp[x1][y1]=0;
while(!q.empty()){
for(int i=0;i<8;i++){
int dx=xx[i]+q.front().x;
int dy=yy[i]+q.front().y;
if(dx>=1&&dx<=m&&dy>=1&&dy<=n&&vis[dx][dy]==0){
q.push(A{dx,dy,q.front().cnt+1});
vis[dx][dy]=1;
mp[dx][dy]=q.front().cnt+1;
}
}
q.pop();//用完后出队列
}
re(i,1,m){
re(j,1,n){
//cout<<mp[i][j]<<" ";
printf("%-5d",mp[i][j]);
}
cout<<endl;
}
return 0;
}
P1259 黑白棋子的移动(递归 类似汉诺塔找规律)(难)
每次必须同时移动相邻的两个棋子,颜色不限,可以左移也可以右移到空位上去,但不能调换两个棋子的左右位置。每次移动必须跳过若干个棋子(不能平移),要求最后能移成黑白相间的一行棋子。
#include<iostream>
using namespace std;
int n,st,sp;// sp上次留下的空位置
char c[101];
void print() //打印
{
int i;
cout<<"step "<<st<<':';
for (i=1;i<=2*n+2;i++) cout<<c[i];
cout<<endl;
st++;
}
void init(int n) //初始化
{
int i;
st=0;
sp=2*n+1;
for (i=1;i<=n;i++) c[i]='o';
for (i=n+1;i<=2*n;i++) c[i]='*';
c[2*n+1]='-';c[2*n+2]='-';
print();
}
void move(int k) //移动一步
{
int j;
for (j=0;j<=1;j++)
{
c[sp+j]=c[k+j];
c[k+j]='-';
}
sp=k;//上个空位
print();
}
void mv(int n) //主要过程
{
int i,k;
if (n==4) //n等于4的情况要特殊处理
{
move(4); move(8); move(2); move(7); move(1);
}
else
{
move(n); move(2*n-1); mv(n-1);
}
}
int main()
{
cin>>n;
init(n);
mv(n);
}
最后4步
P1969 积木大赛(实验7)
q求最小操作
#include <iostream>
using namespace std;
/*
5
2 3 4 1 2
5
*/
int main()
{
int n,a,last=0,ans=0;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a;
if(a>last)ans+=(a-last);
cout<<ans<<" ";
last=a;
cout<<last<<endl;
}
cout<<ans<<endl;
}
P1318 积水面积(c++?)
#include<bits/stdc++.h>
//万能头大法好
using namespace std;
int main()
{
int a[10001]={0},l[10001]={0},r[10001]={0},n,sum=0; //数组清零&定义n和答案
//a用来储存原数据,l用来储存从左到右的最大值,r用来储存从右到左的最大值
cin>>n;
for(int i=1; i<=n; i++)
{
cin>>a[i]; //读入原数据
l[i]=max(l[i-1],a[i]);
//这里很重要!这是至i为止左边最高的高度
}
for(int i=n; i>=1; i--)
r[i]=max(r[i+1],a[i]);
//至i为止右边最高的高度
for(int i=1; i<=n; i++)
{
if(min(l[i],r[i])-a[i]<0) sum+=0;
//如果为负数,则说明当前的比较高,不可能积水,所以把0加上/或者不加0
else sum+=min(l[i],r[i])-a[i];
//因为只能积水到最低的高度处,所以用min。然后再减掉原来的高度,就可以等于i处砖块以上水的面积,如果是0的话也不用特判~
}
cout<<sum; //输出一下答案
return 0;
}
P1048 采药(实验8 )🧡
01背包问题
for(int j = t;j >=c;j--){
dp[j] = max(dp[j-c]+v,dp[j]);
}
#include<bits/stdc++.h>
using namespace std;
int m,t;
int dp[1005];
int c;//时间
int v;//价值
int main(){
cin>>t>>m;
for(int i=1;i<=m;i++){//物品序号
cin>>c>>v;//和上一个物品没有关联
for(int j=t;j>=c;j--){//时间
dp[j]=max(dp[j-c]+v,dp[j]);
//倒退 有这个或没有
}
}
cout<<dp[t];
return 0;
}
差分
(对区间数组加同时减,降低算法复杂度)
类似(#444. 铺地毯 沐风oj)
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1e6+7;
ll a[N];//原数组
ll b[N];//差分数组
ll s[N];//差分数组的前缀和
ll res[N];//对原数组进行m次操作后的结果
/*
5 1
9 1 2 5 3
2 4 1
O(n)
s 0 1 1 1 0 0
b 0 1 0 0 -1 0
*/
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
while (m--) {
ll x, y, z;
scanf("%lld%lld%lld", &x, &y, &z);
b[x] += z;
b[y+1] -= z;//抵消,之后没有
}
for (int i = 1; i <= n; ++i) s[i] = s[i-1] + b[i];
for (int i = 1; i <= n; ++i) res[i] = a[i] + s[i];
for (int i = 1; i <= n; ++i) cout << res[i] << " ";
return 0;
}
王道上机书
1暴力
枚举
反序数+注意初试化
#include<iostream>
using namespace std;
int rev(int x){
int re=0;//注意初始化为0!!!
while(x!=0){
re*=10;
re += x%10;
x/=10;
}
return re;
}
int main(){
for(int i =1000;i<=9999;i++){
if(i*9 == rev(i)){
cout<<i<<endl;
}
}
}
模拟
画图:模拟数组
寻找规律啊
题目:Hello World for U#
n2随长度增加呈现出一个有规律的数列,3个一组
从最短长度5开始,n2为:3,4,3,4,5,4,5,6,5,6,7,6,7,8,7,8,9,8,9,10......
int suren2(int len){//计算n2
if(len%3==0||len%3==2) return len/3+2;
else return len/3+1;
}
日期问题
#include<iostream>
using namespace std;
//日期问题
int year,month,day;
int ans=0;
int daytab[2][13]={
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
};
bool isLeapyear(int year){//4 并且 100 或400
return (year%4==0&&year%100==0)||(year%400==0);
}
int main(){
while(scanf("%d%d%d",&year,&month,&day) != EOF){
//循环输入!!!
for(int i=1;i<month;i++){
ans+=daytab[isLeapyear(year)][i];
}
ans+=day;
cout<<ans;
}
}
其它模拟
区间除树问题:最后可以不用遍历,当为1变false,数量–;
手机键盘:注意建立模拟记录数组,keytable【】
输入
scanf
%c 读入一个字符
%d 读入十进制整数
%s 读入一个字符串,遇空格、制表符或换行符结束。
输出
yyyy-mm-dd形式可能有0格式凑0
printf("%04d-%02d-%02d\n",year,month,day);
2二分查找
KY199 查找
#include<iostream>
#include<algorithm>
using namespace std;
int n, m;
const int MAXN = 1000;
int a[MAXN];
bool BinarySearch(int target, int n) {
int l = 0;
int r = n;
while (l <= r) {
int middle = (l + r) / 2;
if (a[middle] < target) {
l = middle + 1;
} else if (a[middle] > target) {
r = middle - 1;
} else {
return true;
}
}
return false;
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
sort(a, a + n);
cin >> m; //查找个数
for (int i = 0; i < m; i++) {
int t;
cin >> t;
if (BinarySearch(t, n)) {
cout << "YES" << endl;
} else cout << "NO" << endl;
}
return 0;
}
/*
4
9 8 17 6
*/
3字符串相关知识
string s = "How are you?";
s.erase(s.begin(),s.begin()+4);//are you?
t.erase(index,num);
string s1 = s.insert(0, "How "); //How do you
string s = "What's your name?";
s.replace(0, 2, "tt");//ttat’s your name?
//replace起始位置,个数,替换字符串的值
s.find()//返回查找位置未找到返回string::npos
if(t.find("ewq")==string::npos){
cout<<"nofind";//未查找到为string::nops
}
cout<<t.substr(1,3);//截取字符串(开始,个数)
string str;
getline(cin,str);
cout<<str;//getline可以不考虑空格
//反转
reverse(s.begin(),s.end());
//'a'和'z'相差25
cout<<'A'-'a'; //-32
str[i] = (str[i]-'A'-5+26)%26+'A';
//加减循环算法,+26%26可以抵消不用分情况
KY127 统计字符(字符存储)
#include <iostream>
#include <algorithm>
using namespace std;
string str1,str2;
int number[128];
int main()
{
while(getline(cin,str1)){
if(str1=="#"){
break;
}
getline(cin,str2);
for(int i=0;i<128;i++)number[i]=0;//注意清0
for(int i=0;i<str2.length();i++){
number[str2[i]]++;
}
for(int i=0;i<str1.length();i++){
cout<<str1[i]<<" "<<number[str1[i]]<<endl;
}
}
}
/*
I
THIS IS A TEST
i ng
this is a long test string
#
I 2
i 3
5
n 2
g 2
*/
4贪心
01背包
部分包
购买贺年卡(注意不需要时候break)
硬币问题(从最大循环一次,int t = min(a[i],sum/v[i]); 全部或者可以买的)
删数问题(使用字符处理,每次删除后删除递增最后一个数 最后删除最后一个数 s.erase(0,1);)
拦截导弹问题添加链接描述(最长递增递减子序列 不要忘记初值设置为1)
排队接水(最小的先接水,可以先举例子,ans+=num[i-1].T*(n-i))
均分纸牌(用绝对值思想,初始化为平均值的差值)
if(num[i]!=0){
sum++;//次数+1
num[i+1]+=num[i];//更新移动后后一堆纸牌数
}
senior‘s gun (模拟)
for(int i=0;i<n;i++){
if((i>=m)&&gun[i]>=monstor[i]){
//还有怪物射杀并且可以杀死怪物
break;
}
ans+=(gun[i]-monstor[i]);
}
KY4 代理服务器(未完成)
区间贪心🧡
每次选择结束时间最早的,让后面空余时间最多
#include<iostream>
#include<algorithm>
using namespace std;
struct node{
int l,r;
}a[105];
bool camp(node a, node b){
return a.r<b.r;
}
int n;
int ans=1;//注意题意
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].l>>a[i].r;
}
sort(a,a+n,camp);
int temp = a[0].r;//最早结束时间
for(int i=1;i<n;i++){
if(a[i].l>=temp){//开始时间>=结束时间
ans++;
temp=a[i].r;
}
}
cout<<ans<<endl;
}
/*
12
1 3
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
5 10
4 14
2 9
0
*/
KY155 To Fill or Not to Fill(代码量较多学思想)
贪心,每次选油价最低的,然后对路程进行覆盖
注意细节!
if(fill == 1)
if(fill = 1 )
#include<iostream>
#include<algorithm>
/*
测试用例
50 1300 12 8
6.00 1250
7.00 600
7.00 150
7.10 0
7.20 200
7.50 400
7.30 1000
6.85 300
50 1300 12 2
7.10 0
7.00 600
输出结果
749.17
The maximum travel distance = 1200.00
*/
using namespace std;
struct Station{
double price;
int distance;
};
bool camp(Station a, Station b){
return a.price<b.price;
}
int main(){
int cmax,d,davg,n; //最大容量,行驶距离, 每单位油可行驶距离,站数量
while(cin>>cmax>>d>>davg>>n){
Station station[n];
bool flag[d+1];//标记是否走过
for(int i=1;i<=d;i++)flag[i]=0;
for(int i=0;i<n;i++){
cin>>station[i].price>>station[i].distance;
}
sort(station,station+n,camp);
double currentprice = 0;
for(int i=0;i<n;i++){
int currentdistance=0;//此站台加油后行驶的距离
for(int j=station[i].distance+1;j<=cmax*davg+station[i].distance;j++){
if(flag[j]==false){
flag[j]=true;
currentdistance++;
}
//行驶完成或油用完
if(j==d||j==station[i].distance+cmax * davg){
currentprice += station[i].price*currentdistance/(davg*1.0);
break;
}
}
}
int fill = 1;//是否走完
double journey=0;
for(int i=1;i<=d;i++){
if(flag[i]==true)journey++;
else {
fill = 0;
break;
}
}
if(fill==1){
printf("%.2f-完成\n",currentprice);
}else{
printf("%.2f-未完成\n",journey);
}
}
}
5递归与分治
汉诺塔
假设从一个状态到另一个状态要f(n),总结出f(n)和f(n-1)表达式,同时有最终结果值
#include<iostream>
using namespace std;
/*
1
3
12
*/
long long f(int x){
if(x==1){
return 2;
}else {
return 3*f(x-1)+2;
}
}
int main(){
int n;
while(~scanf("%d",&n) ){
cout<<f(n)<<endl;
}
return 0;
}
铺瓷砖(递归)
用1和2组成n可能排列
#include<bits/stdc++.h>
using namespace std;
/*
4
5
*/
int sum;
int n;
void f(int x){
if(x==n){
sum++;
}
if(a>n){//加二的情况过了
return;
}
f(x+1);
f(x+2);
}
int main(){
cin>>n;
f(0);
cout<<sum;
return 0;
}
树节点分治
#include<iostream>
using namespace std;
/*
3 12
0 0
4
*/
int f(int m,int n){
if(m>n){
return 0;
}else{
return 1+f(m*2,n)+f(m*2+1,n);
}
}
int main(){
int m,n;//根 节点 从根到节点有多少个
while(scanf("%d %d",&m,&n) !=EOF){
cout<<f(m,n)<<endl;
}
return 0;
}
6动态规划
最大连续子列和
pri
dp[i]=max(dp[i-1]+a[i],a[i]);
//dp含义:以a[i]结尾的最大子列和
最长递减子序列并不是连续的
dp[i]以a[i]结尾的最大递减子序列
for(int i=0;i<n;i++){
dp[i] = 1;//初始化为1
for(int j=0;j<i;j++){
if(a[j]>=a[i]){
dp[i] = max(dp[j]+1,dp[i]);
}
}
ans = max(dp[i],ans);
}
最长公共子序列
状态转移方程为:
当s1[i] = s2[j]时: dp[i][j] = dp[i-1][j-1]+1;
当s1[i] != s2[j]时: dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
/*
abcd
cxbydz
2
*/
const int maxn = 1001;
string s1;
string s2;
int main(){
while(cin>>s1>>s2){
int n = s1.length();
int m = s2.length();
int dp[n+1][m+1];
memset(dp,0,sizeof(dp));
//dp[i+1][j+1]
//以s1[i]为结尾和以s2[j]为结尾村存在的最长公共子序列
int ans =0;
for(int i=1;i<=n;i++){//从一开始用于防止i-1约为
for(int j=1;j<=m;j++){
if(s1[i-1] == s2[j-1]){
//dp向后移动一个位置防止i-1越界
dp[i][j] = dp[i-1][j-1]+1;
}else{
dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
}
}
}
cout<<dp[n][m]<<endl;
}
}
最长回文串
#include<iostream>
#include<cstring>
const int maxn = 1010;
char S[maxn];
int dp[maxn][maxn];
int main()
{
gets(s);
int len = strlen(S), ans = 1;
memset(dp, 0, sizeof(dp)); //dp数组初始化
//边界
for(int i = 0; i<len; i++)
{
dp[i][i] = 1;
if(i < len - 1)
{
if(S[i] == S[i+1])
{
dp[i][i + 1] = 1;
ans = 2; //初始化时注意当前最长回文子串长度
}
}
}
//状态转移方程
for(int L = 3; L<=len; L++)
{
for(int i = 0; i + L - 1<len; i++)
{
int j = i + L - 1;
if(S[i] == S[j]&&dp[i + 1][j - 1] == 1)
{
dp[i][j] = 1;
ans = L; //更新最长回文子串长度
}
}
}
printf("%d\n", ans);
return 0;
}
合并石头(区间)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
//#define int long long
using namespace std;
const int maxn=1e6+5;
int dp[1005][1005],sum[maxn],a[maxn];
int main(){
memset(dp,0x3f3f3f3f,sizeof(dp));
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
sum[i]=sum[i-1]+a[i];
dp[i][i]=0;
}
for(int len=2;len<=n;len++){
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
for(int k=i;k<=j;k++){
dp[i][j]=
min(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1],dp[i][j]);
}
}
}
cout<<dp[1][n]<<endl;
}
背包问题
01背包模板
for(int i=0;i<n;i++){
cin>>p>>v;//价格 价值
for(int j=c;j>=p;j--){
dp[j] = max(dp[j-p]+v,dp[j]);
}
}
c是可以出卖的资源如金钱或者背包容量
p是获得物品i要付出的代价同上
v是获得物品i获得的利益
#include<iostream>
#include<cstring>
using namespace std;
int c,n;
int ans = 0;
int main(){
while(cin>>c>>n){
int p;//价格
int v;//价值
int dp[c+1];
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++){
cin>>p>>v;//价格 价值
for(int j=c;j>=p;j--){
dp[j] = max(dp[j-p]+v,dp[j]);
}
}
cout<<dp[c];
}
}
饥饿的牛(hunger)(选择不重复区间得到最大)
dp[j] = max(dp[j],dp[s[i].x - 1] + s[i].total)
#include<bits/stdc++.h>
#include<map>
using namespace std;
//this is a book
struct node{
int x,y,total;
};
const int maxn = 2005;
int dp[maxn];
bool cmp(node a,node b){
return a.y<b.y; //按照最后一位从小到大排序
}
int main()
{
int n;
cin>>n;
node a[n];
int x,y;
for(int i=0;i<n;i++){
cin>>a[i].x>>a[i].y;
a[i].total=a[i].y-a[i].x+1;
}
sort(a,a+n,cmp);
for(int i=0;i<n;i++){
for(int j = a[n-1].y;j>=a[i].y;j--){
dp[j] = max(dp[j],dp[a[i].x-1]+a[i].total);
}
}
cout<<dp[a[n-1].y];
return 0;
}
矩阵连乘(计算最小值)(思考)
KY14 最小邮票数
求最小,初始值为最大
if(j==v)dp[j]=1;//初值
else dp[j] = min(dp[j-v]+1,dp[j]);
#include<iostream>
#include<cstring>
/*
10
5
1 3 3 3 4
3
*/
using namespace std;
const int INF = 0x3ffffff;
int c,n;
int main(){
int v;//价值
cin>>c>>n;
int dp[c+1];
for(int i=0;i<=c+1;i++){
dp[i]=INF;
}
for(int i=0;i<n;i++){
cin>>v;//价值
for(int j=c;j>=v;j--){
if(j==v){
dp[j]=1;//初值
}
else dp[j] = min(dp[j-v]+1,dp[j]);
}
}
if(dp[c]!=INF){
cout<<dp[c];
}else
cout<<0;
}
完全背包(每个物品有无穷数量)
和01相同,不过顺序计算
for(int j=p;j<=c;j++)dp[j] = min(dp[j-p]+v,dp[j]);
多重背包
多一层循环
优化 …2^c
#include<iostream>
#include<cstring>
using namespace std;
int v[510],w[510],s[510],dp[6100];
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>v[i]>>w[i]>>s[i];
for(int i=1;i<=n;i++){
for(int j=m;j>=1;--j){
for(int k=0;k<=s[i]&&j>=k*v[i];k++){
//第j个物品 拿k个
dp[j] = max(dp[j],dp[j-k*v[i]]+k*w[i]);
}
}
}
cout<<dp[m];
}
7.1数组栈队列
vector 数组
for(int i = 0;i<a.size();i++)cout<<a[i];//输出
for(vector<int>::iterator it = a.begin();it!=a.end();it++)cout<<*it;//输出
vector<int>::iterator it = a.begin()+1;
a.insert(it,4);//出入 4数组下标
a.erase(a.begin()+1);//删除
queue push pop front() empty
stack push pop top empty() size
#include<iostream>
#include<queue>
using namespace std;
/*
8 3 4
1 2 3 4 5 6 7 8
*/
int n,p,m;//小孩 开始 间隔
queue<int>a;
int main()
{
cin>>n>>p>>m;
for(int i=1;i<=n;i++){
a.push(i);
}
for(int i=1;i<p;i++){
a.push(a.front());
a.pop();
}
int num = n;
int i=0;
while(num){
int cnt = m-1;
while(cnt--){
a.push(a.front());
a.pop();
}
//出队
cout<<a.front();
a.pop();
num--;
}
}
//第一个数字为所有事件
//第一个元素为1,代表进入收容所,第二个元素为动物的编号,正数代表狗,负数代表猫
//第一个元素为2,代表收养,第二个元素为0,代表所有与动物的先后,第二个数字为1,代表收养狗,若为-1
//代表是收养猫 。 不合法的直接忽略 , 输出动物的的编号 , 以空格间隔;
#include<cstdio>
#include<queue>
using namespace std;
struct Animals{
int num;//编号
int seq;//序号
};
int main(){
int seq = 0;
int n;//问题的规模
scanf("%d",&n);
queue<Animals> dogQue;
queue<Animals> catQue;
for(int i = 0 ; i < n ; ++ i ){
int x , y;
scanf("%d%d",&x,&y);
if(1 == x){//动物进入收容所
if( y > 0){
Animals dog;
dog.num = y;
dog.seq = seq ;
++seq;
dogQue.push(dog);
}
else{
Animals cat;
cat.num = y;
cat.seq = seq ;
++seq;
catQue.push(cat);
}
}
else{//动物出收容所
if( 0 == y ){//所有动物先后
if(catQue.empty() && dogQue.empty()){//猫狗都空 忽略
continue;
}
else if (catQue.empty() && !dogQue.empty()
|| !dogQue.empty() && !catQue.empty() && dogQue.front().seq < catQue.front().seq){
//所有动物先后 ,零养狗的情况 1.猫空 狗不空 2.猫狗都不空但是 狗的seq<猫的seq
printf("%d ",dogQue.front().num);
dogQue.pop();
}
else{
printf("%d ",catQue.front().num);
catQue.pop();
}
}
else if(1 == y){//领养狗
if(dogQue.empty()){
continue;
}
else{
printf("%d ",dogQue.front().num);
dogQue.pop();
}
}
else{//领养猫
if(catQue.empty()){
continue;
}
else{
printf("%d ",catQue.front().num);
catQue.pop();
}
}
}
}
}
//括号匹配
#include<iostream>
#include<stack>
using namespace std;
/*
)(rttyy())sss)(
*/
int main()
{
stack<int>a;
string t;
cin>>t;
for(int i=0;i<t.length();i++){
//cout<<t;
if(t[i]>='a'&&t[i]<='z'){
t[i]=' ';
}
}
for(int i=0;i<t.length();i++){
if(t[i]=='('){
a.push(i);
}
if(t[i]==')'){
if(a.empty()){
t[i]='?';
}
if(!a.empty()){//符合
t[a.top()]=' ';
t[i]=' ';
a.pop();
}
//cout<<t;
}
//cout<<t;
}
while(!a.empty()){
t[a.top()]='$';
a.pop();
}
cout<<t;
}
7.2数据结构(排序树、搜索树)
KY212 二叉树遍历
#include<iostream>
#include<stack>
using namespace std;
/*
)(rttyy()sss)(
*/
void treeprint(string a,string b){
if(a.size()!=0){
int r = b.find(a[0]);//头所在中序位置
treeprint(a.substr(1,r),b.substr(0,r));//左
treeprint(a.substr(r+1),b.substr(r+1));//右
cout<<a[0];
}
}
int main()
{
string a,b;
//a 头左 右
//b 左 头右
// r
//输出 左右头
while(cin>>a>>b){
treeprint(a,b);
}
return 0;
}
先序遍历->中序遍历
使用数组
#include<iostream>
#include<stack>
using namespace std;
/*
abc##de#g##f###
c b e g d f a
先序遍历->中序遍历
*/
const int N1=1e8+5;
const int N2=1e2+5;
int pos;
char tree[N1];
char str[N1];
void create(int pos){//创建树
char c = str[t++];
if(c=='#'){
return;
}
tree[pos]=c;//赋值
create(2*pos);//更换位置
create(2*pos+1);
}
void traverse(int root){
if(tree[root]==0){
return;
}
traverse(2*root);
cout<<tree[root]<<" ";
traverse(2*root+1);
}
int main()
{
while(cin>>str){
pos=0;
create(1);
traverse(1);
cout<<endl;
}
}
KY207 二叉排序树
//Binary Search Tree
#include<iostream>
#include<cstdio>
using namespace std;
struct TreeNode{
int data;
TreeNode* leftChild;
TreeNode* rightChild;
TreeNode(int x): data(x),leftChild(NULL),rightChild(NULL){}
};
TreeNode* Insert(TreeNode* root,int x,int father){
if(root==NULL){
root = new TreeNode(x);
cout<<father<<endl;
}
else if(x<root->data){
root->leftChild = Insert(root->leftChild,x,root->data);
}
else if(x>root->data){
root->rightChild = Insert(root->rightChild,x,root->data);
}
return root;
}
int main(){
int N;
int temp;
TreeNode* root = NULL;
cin>>N;
for(int i=0; i<N; i++){
cin>>temp;
root = Insert(root,temp,-1);
}
return 0;
}
KY223 二叉搜索树
#include <iostream>
#include <queue>
using namespace std;
//二叉排序树二
struct TreeNode{
int data;
TreeNode *leftchild;
TreeNode *rightchild;
TreeNode(int x):data(x),leftchild(NULL),rightchild(NULL){}
};
void visit(TreeNode *t){
cout<<t->data<<" ";
}
void PreOrder(TreeNode *root){
if(root==NULL)return;
visit(root);
PreOrder(root->leftchild);
PreOrder(root->rightchild);
}
void InOrder(TreeNode *root){
if(root==NULL)return;
InOrder(root->leftchild);
visit(root);
InOrder(root->rightchild);
return;
}
void PostOrder(TreeNode *root){
if(root==NULL)return;
PostOrder(root->leftchild);
PostOrder(root->rightchild);
visit(root);
return;
}
TreeNode* Insert(TreeNode *root,int x){
if(root==NULL){
root=new TreeNode(x);
}
if(x==root->data);
else if (x<root->data)root->leftchild=Insert(root->leftchild,x);
else root->rightchild=Insert(root->rightchild,x);
return root;
}
int main(){
int n;
while(cin>>n){
TreeNode *root=NULL;
while(n--){
int x;
cin>>x;
root=Insert(root,x);
}
PreOrder(root);
printf("\n");
InOrder(root);
printf("\n");
PostOrder(root);
printf("\n");
}
return 0;
}
KY124 二叉搜索树
描述
判断两序列是否为同一二叉搜索树序列
输入:
2
567432
543267
576342
0
输出:
YES
NO
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
struct node
{
char val;
node *lchild, *rchild;
node(int v):val(v), lchild(NULL), rchild(NULL){}
};
bool flag;
node *insert(node *root, int x) //依次插入每个值
{
if (root == NULL) //节点为空时,插入此节点
{
root = new node(x);
return root;
}
if (root->val > x) //如果这个值小于左儿子
root->lchild = insert(root->lchild, x); //递归向下寻找相应的位置,回溯赋值,构建链表
else //同理
root->rchild = insert(root->rchild, x);
return root;
}
void check(node *root, node *judge) //传入标准树和判断树的地址
{
if (root != NULL && judge != NULL) //如果两个地址都不为空,即此节点两棵树都有值,只需判断值是否相同
{
if (root->val != judge->val) //判断此节点的值是否和标准树一致
flag = false;
check(root->lchild, judge->lchild); //前序遍历
check(root->rchild, judge->rchild);
}
else if (root != NULL || judge != NULL) //如果两颗树中,只有一棵树在这个节点有值,那么这两棵树肯定不一致
flag = false;
}
int main()
{
int n;
while (cin>>n)
{
node *root = NULL; //标准树地址为空
string s;
cin >> s;
for (int i = 0, len = s.length(); i < len; i++) //构建一颗标准树
root = insert(root, s[i]);
for (int i = 0; i < n; i++) //n组数据
{
flag = true; //标记
node *judge = NULL; //判断树地址为空
cin >> s;
for (int i = 0, len = s.length(); i < len; i++) //构建判断树
judge = insert(judge, s[i]);
check(root, judge); //中序遍历,检查两棵树是否一致
if (flag)
printf("YES\n");
else
printf("NO\n");
}
}
}
7.3树形结构(priority_queue< int >q、map、set)
priority_queue< int >q push
top
int main(){
int a[] = {2,1,2,3,4,7};
priority_queue<int>b;
for(int i=0;i<6;i++){
b.push(a[i]);
}
while(b.empty()==false){
cout<<b.top();
b.pop();
}
}
底层默认大根堆,可以修改为小根堆
priority_queue<int>q;
==priority_queue<int,vector<int>,less<int> >q;
priority_queue<int,vector<int>,greater<int> >q;
//greater<int> 表示数字小的优先级越大。
结构体
// f1使用friend其排序与sort排序刚好相反
struct node
{
string name;
int price;
//friend bool operator<(const node &f1,const node &f2)
//建议使用上面的引用来提高效率
friend bool operator<(node f1,node f2)
{
return f1.price<f2.price;
}
};
priority_queue<node> q;
// f2不使用 friend 友元将它写在外面
friend bool operator<(node f1,node f2)
{
return f1.price<f2.price;
}
map
查找学生信息(麻烦了)
#include<iostream>
#include<map>
using namespace std;
int main(){
int n, m;
cin >> n;
map<string, string> students;
for(int i = 0; i < n; i ++){
string id, name, gender, age;
cin >> id >> name >> gender >> age;
students[id] = id + " " + name + " " + gender + " " + age;
}
cin >> m;
string target;
for(int i = 0; i < m; i ++){
cin >> target;
if(students.find(target) != students.end())
//if(students[target]!="")
cout<< students[target] << endl;
else cout<<"No Answer!"<<endl;
}
return 0;
}
KY146 魔咒词典
#include<iostream>
#include<cstdio>
#include<string>
#include<map>
using namespace std;
int main(){
int d,l;
string s;
map<string,string> mp;
while(getline(cin,s)){
if(s=="@END@") break;
d=s.find("]");//定位指定字符的位置,为了将字符串分割
l=s.length();
string m=s.substr(0,d+1);//利用substr提取相关字符串
string n=s.substr(d+2,l);
mp[m]=n;//将分开的两段字符串分别映射成键和值
mp[n]=m;
}
int n;
scanf("%d",&n);
getchar();
string t;
while(n--&&getline(cin,s)){
if(mp[s]==""){//如果没有找到相关的映射
cout<<"what?"<<endl;
}else{
t=mp[s];
if(t.find("[")!=-1){//如果找到的字符串里含有指定字符,就利用substr去除,为-1时未找到
cout<<t.substr(1,(t.length()-2))<<endl;
}else{//如果没有就直接输出
cout<<t<<endl;
}
}
}
return 0;
}
set容器(去重排序)
set<int> q; //以int型为例 默认按键值升序
set<int,greater<int>> p; //降序排列
int x;
q.insert(x); //将x插入q中
q.erase(x); //删除q中的x元素,返回0或1,0表示set中不存在x
q.clear(); //清空q
q.empty(); //判断q是否为空,若是返回1,否则返回0
q.size(); //返回q中元素的个数
q.find(x); //在q中查找x,返回x的迭代器,若x不存在,则返回指向q尾部的迭代器即 q.end()
第k小数
#include<bits/stdc++.h>
using namespace std;
int main()
{
set <int> s;
int n,k;
cin>>n>>k;
int a;
for(int i=0;i<n;i++)
{
cin>>a;
s.insert(a);
}
if(k>=s.size())
{
cout<<"NO RESULT";
return 0;
}
set<int>::iterator it;
it=s.find(k);
cout<<*it;
return 0;
}
8图论(并查集、最小生成树、最短路径)
KY175 连通图
使用并查集
Init() 初始化 find()找到父节点 Union()合并集合
#include<cstdio>
using namespace std;
#define N 1000
int father[N];//存储了父亲的下标
int high[N];//存储了某个根的树的高度
void Init(int n){
//最开始每个元素单独构建一个集合,每个集合是一棵树
for (int i = 1; i <= n; ++i) {
//i的编号
father[i]=i;//每个结点都是树的根
high[i]=1;
}
}
int find(int x){
if (x!=father[x]){
//find的路径压缩,找到祖先以后先不返回,而是设为自己的新父亲。
father[x]= find(father[x]);
}
//x就是树的根
return father[x];
}
void Union(int x, int y, int &num){
x= find(x);
y= find(y);
if (x!=y){
//x y原本不属于同一个连同子图
--num;
}
if (high[x]<high[y]){// y更深
father[x]=y;
} else if (high[x]>high[y]){
father[y]=x;
} else{ // 一样深度
father[y] = x;
++high[x];
}
}
int main(){
int n;
int m;
while (scanf("%d%d",&n,&m)!=EOF){
if (m==0&&n==0){
break;
}
Init(n);
int num=n;//连通子图的数量
for (int i = 0; i < m; ++i) {
int x,y;
scanf("%d%d",&x,&y);
Union(x,y,num);
}
if (num==1){
printf("YES\n");
} else{
printf("NO\n");
}
}
}
Kruskal 算法(最小生成树)
KY148 还是畅通工程
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 1000;
struct Edge{ //定义边结构体
int from;
int to;
int length;
};
Edge edge[MAXN * MAXN]; //边的数量是定点数量的平方
int father[MAXN]; //用以表示每个节点的父节点是什么样的
int height[MAXN];
void Initial(int n){ //n个节点
for(int i = 0; i < n; ++i){ //初始状态下,每个节点的父节点是其自身
father[i] = i;
height[i] = 0;
}
return;
}
int Find(int x){
if(x != father[x]){ //表示当前节点不是根节点,需要继续向上查找
father[x] = Find(father[x]); //将路径压缩,可以提高查找效率
}
return father[x];
}
void Union(int x,int y){ //合并,将一棵树作为另一棵树的子树
x = Find(x); //找到x的根节点
y = Find(y); //找到y的根节点
if(x != y){ //说明这两个节点本身不属于同一个集合,需合并
if(height[x] < height[y]){ //将树高较高的作为树高较低的子树进行合并
father[x] = y;
}else if(height[x] > height[y]){
father[y] = x;
}else{ //高度相同时,可任意连接
father[y] = x;
height[x]++; //树的高度递增1
}
}
}
bool Compare(Edge x,Edge y){
return x.length < y.length;
}
int Kruskal(int n,int edgeNum){
Initial(n); //先初始化
sort(edge,edge + edgeNum,Compare); //按照边的权值升序排序
int sum = 0; //统计路径之和
for(int i = 0; i < edgeNum; ++i){
Edge current = edge[i];
if(Find(current.from) != Find(current.to)){
//两个定点不属于同一个集合,就进行合并
Union(current.from,current.to);
sum += current.length;
}
}
return sum;
}
int main(){
int n;
while(scanf("%d",&n) != EOF){
if(n == 0){
break;
}
int edgeNum = n * (n - 1) / 2; //n个顶点,n * (n - 1) / 2条边
for(int i = 0; i < edgeNum; ++i){ //输入边的信息
scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].length);
}
int answer = Kruskal(n,edgeNum);
printf("%d\n",answer);
}
return 0;
}
dijkstra算法的实现单元最短路径
KY107
畅通工程续
已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离。
9深搜广搜
bfs模板
const int dx ={...}//建立方向矩阵
int bfs(node st) {
q.push(st);//初始化压入 d = 0;
while (!q.empty()) {//非空
node now = q.front();
q.pop();//获取头建立temp 然后出栈
vis[]=1//标记已访问
for (int i = 0; i < 6; i ++) {//方向遍历
int x = t.x + dx[i], y = t.y + dy[i]//相加
if (...) continue;//不符合边界条件或者访问过
d = d + 1;//若有求路程+1
if (..) return ..;//结束条件
vis[]=1//标记已访问
// q.push({x,y,z});
node stt;
stt.x = x, stt.y = y, stt.z = z;
q.push(st);//压入
}
}
return 0;
}
int main() {
bfs();
return 0;
}
dfs模板
void dfs(int r,int num){//需要约束的变量
if(..) return ;//约束条件 返回
for(int i = 0;i < n;i ++){
if(..){//满足条件
vis[i] = 1;//标记已经遍历
dfs(r+1,num+1);//搜下一个
vis[i] = 0;//需要复原
}
}
dfs(r+1,num);//其它情况
}
int main()
{
dfs(0,0);
}
994. 腐烂的橘子(BFS)
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
int cnt =0;
int time=0;
int dx[4]={-1, 1, 0, 0};
int dy[4]= {0, 0, -1, 1};
int n = grid.size();
int m = grid[0].size();
queue<pair<int,int>>q;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
//把所有烂橘子扔进队列🧡
if(grid[i][j]==2){
q.push({i,j});
}
if(grid[i][j]==1){
//新鲜橘子总数
cnt++;
//把所有新鲜橘子标记为为访问状态
grid[i][j]=-1;
}
}
}
while(!q.empty()){
//判断当前是否有新的烂橘子产生
bool flag =false;
//当前队列大小
int curSize =q.size();
//循环当前队列中的元素
for(int i=0;i<curSize;i++){
int x=q.front().first;
int y=q.front().second;
q.pop();
//遍历周边橘子
for(int i=0;i<4;i++){
int newX = x+dx[i];
int newY =y+dy[i];
if(newX>=0&&newX<n &&newY>=0&&newY<m&&grid[newX][newY]==-1){
//把新鲜橘子变坏
grid[newX][newY]=2;
//表示当前有烂橘子产生
flag =true;
//同时新鲜橘子个数减少
cnt--;
//把新的烂橘子加入到队列当中去
q.push({newX,newY});
}
}
}
//在本轮循环中如果有烂橘子产生,那么时间点就加1
if(flag) time++;
}
//最后 如果还有新鲜橘子,则说明不可能存在所有的那远哥的新鲜橘子都会烂,否则就返回最小分钟数time
return cnt ? -1 : time;
}
};
swer);
}
return 0;
}
dijkstra算法的实现单元最短路径
KY107
畅通工程续
已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离。
9深搜广搜
994. 腐烂的橘子(BFS)
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
int cnt =0;
int time=0;
int dx[4]={-1, 1, 0, 0};
int dy[4]= {0, 0, -1, 1};
int n = grid.size();
int m = grid[0].size();
queue<pair<int,int>>q;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
//把所有烂橘子扔进队列🧡
if(grid[i][j]==2){
q.push({i,j});
}
if(grid[i][j]==1){
//新鲜橘子总数
cnt++;
//把所有新鲜橘子标记为为访问状态
grid[i][j]=-1;
}
}
}
while(!q.empty()){
//判断当前是否有新的烂橘子产生
bool flag =false;
//当前队列大小
int curSize =q.size();
//循环当前队列中的元素
for(int i=0;i<curSize;i++){
int x=q.front().first;
int y=q.front().second;
q.pop();
//遍历周边橘子
for(int i=0;i<4;i++){
int newX = x+dx[i];
int newY =y+dy[i];
if(newX>=0&&newX<n &&newY>=0&&newY<m&&grid[newX][newY]==-1){
//把新鲜橘子变坏
grid[newX][newY]=2;
//表示当前有烂橘子产生
flag =true;
//同时新鲜橘子个数减少
cnt--;
//把新的烂橘子加入到队列当中去
q.push({newX,newY});
}
}
}
//在本轮循环中如果有烂橘子产生,那么时间点就加1
if(flag) time++;
}
//最后 如果还有新鲜橘子,则说明不可能存在所有的那远哥的新鲜橘子都会烂,否则就返回最小分钟数time
return cnt ? -1 : time;
}
};
``