目录
一、小红的口罩(贪心+优先级队列)



#include<iostream>
#include<queue>
#include<vector>
using namespace std;
int n,k;
int main(){
//用一个小根堆 每次使用不舒适度最小的
cin>>n>>k;
int x;
priority_queue<int,vector<int>,greater<int>> heap;
for(int i=0;i<n;++i){
cin>>x;
heap.push(x);
}
//开始循环
int sum=0,ret=0;
while(sum<=k){
int t=heap.top();
heap.pop();
sum+=t;
if(sum>k)break;
heap.push(t*2);
++ret;
}
cout<<ret<<endl;
}
二**、春游(分类讨论+数学)



先考虑恰好可以每条船坐满,那就是看双人船和三人船每个人A多少钱,哪个小就选哪个。如果人数取模于这个更少A的船的人数刚好等于0,那最好了,那就是我们要的方案。
如果有余,就考虑最后余下的那个人,和前面几个人组合。
如果是双人船的方案,最后要么剩下那个人坐双,要么坐单,要么和前面两个人改坐三人。
如果是三人船的方案,最后要么剩下一个要么剩下两个,这个人要么一个人坐便宜的那个,要么和前面三个加上他自己一个组合,双双,要么双三肯定是双三。或者最后剩下两个人,要么这两人一起坐便宜的那个,要么和前面三个人凑三个双,要么就是一双一三,要么就是两三,一双一三和两三不就是前面那个人单独坐的情况。
所以最终就得到上图绿色的,下面开始编写代码
#include<iostream>
#include<cmath>
using namespace std;
typedef long long LL;
LL t,n,a,b;
LL func(){
if(n<=2) return min(a,b);//做便宜的船
//ceil向上取整
if(a>=b) return (LL)ceil(n/3.0)*b;
LL ret=0;
if(a*3<b*2){//说明双人船便宜 尽量选择双人船
ret+=n/2*a;
if(n%2==1) ret+=min(a,b-a);//多1个人 看看是要直接租双人 还是退掉一个双人租三人
}
else{//说明要尽可能租三人船 b*2<=a*3 有可能b更便宜
ret+=n/3*b;
//此时可能剩1个人或者2个人
n%=3;
if(n==1) ret+=min(a,2*a-b);
if(n==2) ret+=a;
}
return ret;
}
int main(){
cin>>t;
while(t--){
cin>>n>>a>>b;
cout<<func()<<endl;
}
}
三**、数位染色(01背包)


#include <iostream>
#include<vector>
using namespace std;
int main() {
long long x,tmp,sum;
cin>>x;
tmp=x;
sum=0;
vector<int> arr;
while(tmp)
{
arr.push_back(tmp%10);
sum+=tmp%10;
tmp/=10;
}
if(sum%2)cout<<"No";
else{
long long target=sum/2;
int n=arr.size();
vector<vector<bool>> dp(n+1,vector<bool>(target+1));
dp[0][0]=true;
for(int i=1;i<=n;++i)
{
for(int j=arr[i-1];j<=target;++j)
{
dp[i][j]=dp[i-1][j]||dp[i-1][j-arr[i-1]];
}
}
if(dp[n][target])cout<<"Yes";
else cout<<"No";
}
return 0;
}
进一步空间优化,注意要倒着填,否则顺着填当计算dp[j]时,dp[j-arr[i]]可能已经被当前轮次更新过了
#include <iostream>
using namespace std;
const int N=20,M=10*9;//记录所有数位
bool dp[M];//前i个数选 和恰好为target
int arr[N];//标记各个数位
long long x;
int n=0;//统计有多少位
int sum=0;//统计总和
bool func(){
if(sum%2==1) return false;
sum/=2;
dp[0]=true;
for(int i=0;i<n;++i)
for(int j=sum;j>=arr[i];--j)
dp[j]=dp[j]||dp[j-arr[i]];
return dp[sum];
}
int main() {
cin>>x;
while(x){
arr[n++]=x%10;
sum+=x%10;
x/=10;
}
if(func()) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
四、素数回文(模拟+数学)


素数判断前面我们已经掌握了,所以这题就是创建回文数的模拟题
#include <cmath>
#include <iostream>
using namespace std;
bool IsPrime(long long x)
{
if(x%2==0)return false;
for(int i=3;i<=sqrt(x);i+=2)
{
if(x%i==0)return false;
}
return true;
}
int main() {
string t;
cin>>t;
int cur=t.length()-2;
while(cur>=0)
{
t+=t[cur];
--cur;
}
long long x=stoll(t);
// cur=0;
// while(cur<t.length()){
// x=x*10+t[cur]-'0';
// ++cur;
// }
if(IsPrime(x))cout<<"prime";
else cout<<"noprime";
return 0;
}
五、活动安排(贪心+自定义排序/动规)

0位置相等的按1位置降序排,0位置不等的按0位置升序排,dp填表的时候往前找,如果0位置相等跳过,不等的时候就找有没有1位置比我的0位置小的。如果有直接等于该位置长度+1,贪心的break直接将效率最大化
#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
struct cmp{
bool operator()(const vector<int> &a1,const vector<int> &a2){
if(a1[0]==a2[0]){
return a1[1]>a2[1];
}
return a1[0]<a2[0];
}
};
int main() {
int n;
cin>>n;
vector<vector<int>> arr(n,vector<int>(2));
for(int i=0;i<n;++i)cin>>arr[i][0]>>arr[i][1];
sort(arr.begin(),arr.end(),cmp());
// for(int i=0;i<n;++i)cout<<arr[i][0]<<" "<<arr[i][1]<<endl;
vector<int> dp(n+1,1);
for(int i=1;i<=n;++i)
{
for(int j=i-1;j>=1;--j)
{
if(arr[i-1][0]==arr[j-1][0])continue;
if(arr[i-1][0]>=arr[j-1][1])
{
dp[i]=dp[j]+1;
break;
}
}
}
cout<<dp[n];
return 0;
}
或者不用动规,摘自别人的题解
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int,int> PII;
const int N=2e5+1;
int n;
PII arr[N];
int main() {
cin>>n;
for(int i=0;i<n;++i) cin>>arr[i].first>>arr[i].second;
sort(arr,arr+n);
int ret=0,right=arr[0].second;
for(int i=1;i<n;++i)
//如果有重叠 保留右侧最小的
if(arr[i].first<right) right=min(right,arr[i].second);
else{//如果没有重叠 就更新一下 然后++ret
++ret;
right=arr[i].second;
}
cout<<ret+1<<endl;
}
六**、合唱团(多状态dp)

做的时候意识到是多状态dp,但是选择k个同时保证相连两个距离不超过d,不知道如何处理
对于人数我们可以再开一个维度表示选择了几个人,第一个维度代表从[1,i]中挑选的最大乘积,因为有d的限制,所以我们设置为arr[i]为必选,这样dp填表的时候只需要考虑i~max(j-1,i-d)的位置即可。
看完解析的细节问题,感觉这题配得上困难题




代码最终实现如下:
#include <iostream>
#include <vector>
using namespace std;
const long long N=0x3f3f3f3f3f3f3f3f;
int main() {
int n,k,d;
cin>>n;
vector<int> arr(n+1);
for(int i=1;i<=n;++i)cin>>arr[i];
cin>>k>>d;
//f存正,g存负
vector<vector<long long>> f(n+1,vector<long long>(k+1,-N));
vector<vector<long long>> g(n+1,vector<long long>(k+1,N));
//填表
for(int i=1;i<=n;++i)
{
//初始化
f[i][1]=g[i][1]=arr[i];
//从2开始,1我们已经填过了
for(int j=2;j<=min(i,k);++j)
{
for(int prev=i-1;prev>=max(i-d,j-1);--prev)
{
if(arr[i]>0)
{
f[i][j]=max(f[i][j],f[prev][j-1]*arr[i]);
g[i][j]=min(g[i][j],g[prev][j-1]*arr[i]);
}
else if(arr[i]<0)
{
f[i][j]=max(f[i][j],g[prev][j-1]*arr[i]);
g[i][j]=min(g[i][j],f[prev][j-1]*arr[i]);
}
else f[i][j]=g[i][j]=0;
}
}
}
long long ans=-N;
for(int i=k;i<=n;++i)
{
ans=max(ans,f[i][k]);
}
cout<<ans;
return 0;
}
今天三题一遍过
七、跳台阶扩展问题(规律)

#include <iostream>
#include <cmath>
using namespace std;
int main() {
int n;
cin>>n;
cout<<pow(2,n-1);
return 0;
}
八、包含不超过两种字符的最长子串(滑动窗口)

#include <iostream>
#include<unordered_map>
using namespace std;
int main() {
string n;
cin>>n;
int len=0,left=0,right=0;
unordered_map<char,int> hash;
while(right<n.size())
{
char in=n[right];
++hash[in];
while(hash.size()>2)
{
char out=n[left];
if(--hash[out]==0)hash.erase(out);
++left;
}
len=max(len,right-left+1);
++right;
}
cout<<len;
return 0;
}
九、字符串的排列(排序+dfs全排列带去重)


牛客好像不能像力扣一样,成员变量当作全局变量自动初始化,得放在类外
#include <vector>
string path;
bool check[11];
vector<string> ret;
class Solution {
public:
void dfs(string str,int pos)
{
if(pos==str.size())
{
ret.push_back(path);
return;
}
for(int i=0;i<str.size();++i)
{
if(!check[i])
{
if(i==0||str[i]!=str[i-1]||check[i-1])
{
path.push_back(str[i]);
check[i]=true;
dfs(str,pos+1);
path.pop_back();
check[i]=false;
}
}
}
}
vector<string> Permutation(string str) {
if(str=="")return ret;
sort(str.begin(),str.end());
dfs(str,0);
return ret;
}
};
今天三题也拿下了
十、ISBN号码(模拟)


#include <iostream>
using namespace std;
int main() {
string s;
cin>>s;
int cur=0,num=1,sum=0;
while(cur<11)
{
if(s[cur]=='-')
{
++cur;
continue;
}
int x=s[cur]-'0';
sum+=(x*num);
++cur;
++num;
}
int right_num=sum%11;
if(s[12]==right_num+'0'||(s[12]=='X'&&right_num==10))
{
cout<<"Right";
}
else{
s.pop_back();
if(right_num==10)s+='X';
else{
s+=(right_num+'0');
}
cout<<s;
}
return 0;
}
十一、kotori和迷宫(单源最短路bfs)


#include <iostream>
#include <vector>
#include<queue>
using namespace std;
using PII=pair<int,int>;
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
bool check[50][50];
int main() {
int n,m;
cin>>n>>m;
queue<PII> q;
vector<string> grid(n);
for(int i=0;i<n;++i)
{
cin>>grid[i];
for(int j=0;j<m;++j)
{
if(grid[i][j]=='k')
{
check[i][j]=true;
q.push({i,j});
}
}
}
int steps=0,ways=0;
while(!q.empty())
{
int sz=q.size();
if(ways==0)
{
++steps;
}
while(sz--)
{
auto [i,j]=q.front();
q.pop();
for(int k=0;k<4;++k)
{
int x=i+dx[k],y=j+dy[k];
if(x>=0&&x<n&&y>=0&&y<m&&!check[x][y]&&grid[x][y]=='e')
{
++ways;
check[x][y]=true;
continue;
}
if(x>=0&&x<n&&y>=0&&y<m&&!check[x][y]&&grid[x][y]=='.')
{
check[x][y]=true;
q.push({x,y});
}
}
}
}
if(ways)cout<<ways<<" "<<steps;
else cout<<-1;
return 0;
}
十二、矩阵最长递增路径(单源bfs / dfs+记忆化)



bfs解法
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
int _n,_m;
using PII=pair<int,int>;
class Solution {
public:
int bfs(vector<vector<int> >& matrix,int i,int j)
{
int steps=0;
queue<PII> q;
q.push({i,j});
while(!q.empty())
{
int sz=q.size();
++steps;
while(sz--)
{
auto [i,j]=q.front();
q.pop();
for(int k=0;k<4;++k)
{
int x=i+dx[k],y=j+dy[k];
if(x>=0&&x<_n&&y>=0&&y<_m&&matrix[x][y]>matrix[i][j])
{
q.push({x,y});
}
}
}
}
return steps;
}
int solve(vector<vector<int> >& matrix) {
_n=matrix.size(),_m=matrix[0].size();
int len=0;
for(int i=0;i<_n;++i)
{
for(int j=0;j<_m;++j)
{
len=max(len,bfs(matrix,i,j));
}
}
return len;
}
};
dfs记忆化
#include <vector>
int memo[1001][1001],dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
int _m,_n,ret;
class Solution {
public:
int solve(vector<vector<int> >& matrix) {
_m=matrix.size(),_n=matrix[0].size();
for(int i=0;i<_m;++i)
{
for(int j=0;j<_n;++j)
{
ret=max(ret,dfs(matrix,i,j));
}
}
return ret;
}
int dfs(vector<vector<int>>&matrix,int i,int j)
{
if(memo[i][j])return memo[i][j];
int path=1;
for(int k=0;k<4;++k)
{
int x=i+dx[k],y=j+dy[k];
if(x>=0&&x<_m&&y>=0&&y<_n&&matrix[x][y]>matrix[i][j])
{
path=max(path,dfs(matrix,x,y)+1);
}
}
memo[i][j]=path;
return path;
}
};
事不过三,今天三题一题都没AC
十三**、奇数位丢弃(找规律)


对于多组数据直接while(cin>>n)即可
#include <iostream>
using namespace std;
int main() {
int n;
while(cin>>n){
int ret=1;
while(ret-1<=n) ret*=2;
cout<<ret/2-1<<endl;
}
return 0;
}
十四**、求和(dfs)

dfs每层为选择几个,不过已经忘了代码怎么写的了,或者选和不选也可以做,哎呀,看来是大脑偷懒了
选还是不选为决策
#include <iostream>
using namespace std;
bool vis[11];//标记数组
int n,m;
int sum;//统计已选数字的总和
void dfs(int pos){
if(sum==m){
for(int i=1;i<=n;++i)
if(vis[i]) cout<<i<<" ";
cout<<endl;
return;
}
if(sum>m||pos>n) return;//没有意义了
//选
sum+=pos;
vis[pos]=true;
dfs(pos+1);
sum-=pos;
vis[pos]=false;
dfs(pos+1);//不选
}
int main() {
cin>>n>>m;
dfs(1);//dfs枚举位置
}
每层选几个为决策,其实就是求所有子序列
#include <iostream>
using namespace std;
bool vis[11];//标记数组
int n,m;
int sum;//统计已选数字的总和
void dfs(int pos){
if(sum==m){
for(int i=1;i<=n;++i)
if(vis[i]) cout<<i<<" ";
cout<<endl;
return;
}
if(sum>m) return;//没有意义了
for(int i=pos;i<=n;++i){
sum+=i;
vis[i]=true;
dfs(i+1);
vis[i]=false;
sum-=i;
}
}
int main() {
cin>>n>>m;
dfs(1);//dfs枚举位置
}
十五**、计算字符串的编辑距离(dp)


dp神力
#include <iostream>
#include <string>
using namespace std;
const int N=1010;
int dp[N][N];
string a,b;
int main() {
//dp[i][j]表示以0-i的A串 变成0-j的B串所需要的最小操作次数
cin>>a>>b;
int m=a.size(),n=b.size();
a=' '+a,b=' '+b;//为了同步下标
dp[0][0]=0;//都是空串
for(int i=1;i<=m;++i) dp[i][0]=i;
for(int j=1;j<=n;++j) dp[0][j]=j;
for(int i=1;i<=m;++i)
for(int j=1;j<=n;++j)
if(a[i]==b[j]) dp[i][j]=dp[i-1][j-1];
else dp[i][j]=min(min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
cout<<dp[m][n]<<endl;
}
十六、提取不重复的整数(模拟)

#include <iostream>
#include<unordered_map>
using namespace std;
int main() {
unordered_map<char,int> hash;
string s;
cin>>s;
int cur=s.length()-1;
string ans;
while(cur>=0)
{
while(cur>=0&&hash.count(s[cur]))--cur;
if(cur<0)break;
++hash[s[cur]];
ans.push_back(s[cur]);
--cur;
}
cout<<ans;
return 0;
}
十七、哈夫曼编码(模板)

#include <iostream>
#include <queue>
using ll=long long;
using namespace std;
int main() {
ll n,x;
cin>>n;
priority_queue<ll,vector<ll>,greater<ll>> hp;
while(n--)
{
cin>>x;
hp.push(x);
}
ll sum=0;
while(hp.size()>=2)
{
ll a=hp.top();
hp.pop();
ll b=hp.top();
hp.pop();
ll num=a+b;
sum+=num;
hp.push(num);
}
cout<<sum;
return 0;
}
十八**、abb(dp+哈希)



感觉难度上了一个level
#include <iostream>
using namespace std;
const int N=1e5+10;
typedef long long LL;
char s[N];
LL f[26],g[26];
//f[]表示以i位置为结尾时有多少个_x
//g[]表示以i位置为结尾时有多少个x
int n;
int main() {
cin>>n>>s;
LL ret=0;
for(int i=0;i<n;++i){
int x=s[i]-'a';
ret+=f[x];
f[x]+=i-g[x];
g[x]+=1;
}
cout<<ret<<endl;
}
本周完结撒花*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。

189

被折叠的 条评论
为什么被折叠?



