不会做的题参考牛客网上其他人的代码,加上自己的理解
Q1.连续最大和
一个数组有N个元素有正有负,找出连续的子数组使得和最大。
说明:以第i个元素结尾,与以第i-1个元素结尾比较连续子数组最大和,
如果用一维数组dp保存以第i个元素结尾时的最大连续子数组和。
dp[i]=max(a[i],dp[i-1]+a[i]);
dp[0]=a[0];
找到dp[i]中最大的即可。
#include<iostream>
#include<algorithm>
#include<limits.h>
using namespace std;
int findmax(int a[], int n)//同样的思想可以写的更简洁,暂时不想写了
{
int dp[n],maxsum=INT_MIN;
for(int i=0;i<n;i++)
{
if(i==0)
dp[0]=a[0];
else
dp[i]=max(a[i],a[i]+dp[i-1]);
}
for(int i=0;i<n;i++)
{
if(dp[i]>maxsum)
maxsum=dp[i];
}
return maxsum;
}
int main()
{
int n, a[100000];
cin >> n;
for (int i = 0; i < n; ++i)
cin >> a[i];
int result=findmax(a,n);
cout << result << endl;
return 0;
}
Q2.餐馆问题
参观有n张桌子,分别能容纳一定人数,有m行人,分别有一定人数,消费一定金额,不允许拼桌,找出最大的收入金额。
下面的代码AC没通过,测试用例通过率90%,问题出在超时了!!!
#include<iostream>
#include<vector>
#include<math.h>
#include<algorithm>
using namespace std;
struct P{
int num;
int val;
P(int x, int y) :num(x), val(y){}
};
bool compare(P p1, P p2)
{
return p1.num<p2.num||p1.num==p2.num&&p1.val<p2.val;
}
vector<int> a;
vector<P> v;
long long result=0;
int main()
{
int n, m,x,y;
cin >> n >> m;
a.resize(n);
for (int i = 0; i < n; i++)
cin >> a[i];
sort(a.begin(),a.end());
for (int i = 0; i < m; i++)
{
cin >> x >> y;
v.push_back(P(x,y));
}
sort(v.begin(), v.end(), compare);
//桌号从小到大,分别找到能赚的最多的钱
for (int i = 0; i < n; i++)
{
int pos = -1,money=0,j=0;
while(a[i] >= v[j].num&&j<m)
{
if (v[j].val>money)
{
money = v[j].val;
pos = j;
}
j++;
}
if (pos != -1)
{
v[pos].val = 0;
}
result += money;
}
cout << result << endl;
return 0;
}
//参考他人代码,用STL中的multiset存储桌子大小信息,并用set带有的函数处理,不超时。。之前没用过set,补了很多相关知识只是看懂了,还需要再练习。。。
#include<iostream>
#include<vector>
#include<math.h>
#include<set>
#include<algorithm>
using namespace std;
struct P{
int num;
int val;
P(int x, int y) :num(x), val(y){}
};
bool compare(P p1, P p2)
{
return p1.val > p2.val || p1.val == p2.val&&p1.num > p2.num;
}
multiset<int> a;
long long result = 0;
int main()
{
vector<P> v;
int n, m,x,y;
cin >> n >> m;
for (int i = 0; i < n; i++)
{
int t;
cin >> t;
a.insert(t);
}
for (int i = 0; i < m; i++)
{
cin >> x >> y;
v.push_back(P(x,y));
}
sort(v.begin(), v.end(), compare);
for (auto i : v)
{
auto table = a.lower_bound(i.num);
if (table != a.cend())
{
result += i.val;
a.erase(table);
}
if (a.empty()) break;
}
cout << result << endl;
return 0;
}
Q3.地下迷宫
n*m的迷宫,入口在(0,0),出口在(0,m-1),上下左右移动,左右移动消耗1点体力值,上移3点,下移0点,初始有一个体力值k。输入n,m,k,然后输入迷宫,找到消耗体力最小的路径并输出。
代码非原创,只是加上注释,方便理解
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int const maxn = 15;
int ans = -1;
int r,c;
int data[maxn][maxn];//保存迷宫图
int vis[maxn][maxn];//与迷宫图同样大小的矩阵,用于辅助记录已走过的点,不走回头路
int dr[]={0,0,-1,1};//四个移动方向
int dc[]={-1,1,0,0};
int dv[]={1,1,3,0};//分别消耗的体力
struct P{//保存坐标
int r;
int c;
P(int a1=0,int a2=0):r(a1),c(a2){}
friend ostream& operator<<(ostream& os,P p1);
};
ostream& operator<<(ostream& os,P p1) {//便于输出
os<<"["<<p1.r<<","<<p1.c<<"]";
return os;
}
vector<P> V,vp;//V保存能到达出口的路径结果,并且会更新保存体力值消耗最小的结果,v则保存递归时的临时结果
int live = -1, init;//live为-1表示没有找到路径,找到时保存剩余体力最大的值
void dfs(int rr,int cc) {
if(rr==0&&cc==c-1&&init>=0) {//达到出口时判断是否是体力消耗值当前最小,是则保存路径和剩余体力值
if(init>=live) {
live = init;
V = vp;
}
} else {
for (int i=0;i<4;i++) {//4个方向都尝试走一步
int nr = rr+dr[i];
int nc = cc+dc[i];
if(nr>=0&&nr<r&&nc>=0&&nc<c&&data[nr][nc]&&!vis[nr][nc]) {//如果没有走出边界而且是之前没有走过的点而且是可走的点
init-=dv[i];//减掉这一步需要消耗的体力
vis[nr][nc]=1;//另一个二维数组只是为了保存已走过的点,在下一次迭代时不会再往回走
vp.push_back(P(nr,nc));//把这个点放到临时的vp中
dfs(nr,nc);//在当前节点上再次dfs,注意如果未走到出口会一直dfs直到无路可走
init+=dv[i];//以下三步表示找到结果或者无路可走了都要退回到起点。
vis[nr][nc]=0;
vp.pop_back();
}
}
}
}
int main() {
cin>>r>>c>>init;
for (int i=0;i<r;i++) {
for(int j=0;j<c;j++) {
cin>>data[i][j];
}
}
vis[0][0]=1;//先把起点放进vp并标志已走过
vp.push_back(P(0,0));
dfs(0,0);
if(live!=-1) {
cout<<V[0];
for (int i=1;i<V.size();i++)
cout<<","<<V[i];
cout<<endl;
} else {
cout<<"Can not escape!"<<endl;
}
}
笔试题里很多dfs之类的题,需要专项练习。。。
Q4.末尾0的个数
输入一个正整数n,求n!(即阶乘)末尾有多少个0? 比如: n = 10; n! = 3628800,所以答案为2
之前遇到过的题,简单说n!=2^n+3^m+5^k+…,理解一下就是由2和5的个数决定,显然2的个数n>k,所以最后0的个数等于k。
遇到5^2就要加2此,5^3就要加3次…
#include<iostream>
using namespace std;
int count=0;
int main()
{
int n;
cin>>n;
n=n-n%5;
while(n)
{
int x=n;
while(n%5==0)
{
count++;
n=n/5;
}
n=x;
n-=5;
}
cout<<count<<endl;
return 0;
}
Q5.进制转换
给定一个十进制数M,以及需要转换的进制数N。将十进制数M转化为N进制数
#include<iostream>//写的比较繁琐,懒得改了。。。题目比较简单
using namespace std;
int main()
{
int M,N;
string s = "ABCDEF";
cin >> M>>N;
if(M<0)
{
cout<<'-';
M/=-1;
}
int a[35], b[35];
int i = 0;
while (M)
{
a[i++] = M%N;
M /= N;
}
for (int j = i - 1; j >= 0; j--)
{
b[i - 1 - j] = a[j];
{
if (b[i - 1 - j] < 10)
cout << b[i - 1 - j];
else
cout << s[b[i - 1 - j] - 10];
}
}
return 0;
}
Q6数字和为sum的方法数
输入n个数,给定一个和m,从这n个数中选出部分数使和为m。(题目表述不清楚,但是如果n个数的和刚好为m也是可以的)
说明:这一题优点类似背包问题,而且在leetcode上刷到过类似的题。
需要用到动态规划的思想。
一个二维dp数组。
dp[i][j]表示以第i个数为结尾,满足和为j的方法数。
转移方程为:
dp[i][j]=dp[i-1][j-a[i]]+dp[i-1][j];(如果a[i]<=j)
这说明结果应该等于以第i-1个数结尾找结果为j-a[i]的方法数+去掉a[i],以第i-1个数为结尾找结果为j的方法数
dp[i][j]=dp[i-1][j];(如果a[i]>j)
这说明a[i]不可用,直接往前推一步即可
dp[0][0]=1;0个数找和为0,显然方法数为1。(dp[0]0”>x=0,显然)
const int MaxN = 1005;
long long int dp[MaxN][MaxN];//注意:第一方法数可能超过Int的范围,第二需要声明全局,不然会stack overflow
int main()
{
int N, M;
cin >> N >> M;
dp[0][0] = 1;
vector<int> vec(N+1);
//vec.resize(N + 1);(如果vec定义为全局就在这里resize限定大小)
for (int i = 1; i <= N; i++)
cin >> vec[i];
for (int i = 1; i <= N; i++)
{
for (int j = M; j >= 0; j--)
{
if (j >= vec[i])
dp[i][j] = dp[i - 1][j - vec[i]] + dp[i - 1][j];
else
dp[i][j] = dp[i - 1][j];
}
}
cout << dp[N][M] << endl;
system("pause");
return 0;
}