尼克的任务 题目链接
暴力做法 用递归的时候慎重开全局变量 …不然会debug几个小时
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
struct aaa{
int st,len;
}a[N];
int f[N];
bool st[N];
int sum,n,ans;
vector<int>v[N];
void dfs(int t,int fre){
if(t==sum+1){
ans=max(ans,fre);
return;
}
int cnt=0;
for(int i=0;i<v[t].size();i++){//枚举这个时间点
dfs(t+a[v[t][i]].len,fre);//去枚举这个任务结束时的时间
cnt++;//相当于flag的作用
}
if(cnt==0) dfs(t+1,fre+1);//如果当前时间点没有任务,那么空闲时间+1,然后去枚举下一个时间点
}
int main(){
cin >> sum >> n;//总时间为sum,总任务数为n
for(int i=1;i<=n;i++){
cin >> a[i].st >> a[i].len;
v[a[i].st].push_back(i);//某个时间点所包含的所有任务
}
dfs(1,0);//从时间点为1,空闲时间为0开始搜索
cout << ans;
return 0;
}
记忆化搜索做法
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
vector<int>v[N];
int n;
int m;
int f[N];
bool st[N];
bool flag;
int dfs(int t,int fr){
if(st[t]){
return f[t];
}
if(t>n){
return 0;
}
if(v[t].size()==0){
f[t]=1+dfs(t+1,fr+1);
}
else{
for(int i=0;i<v[t].size();i++){ //看在这个时间点能否给尼克安排工作
f[t]=max(f[t],dfs(t+v[t][i],fr)); // 去到下一个工作结束时间
}
}
st[t]=true;
return f[t];
}
int main(){
//memset(f,-0x3f3f3f,sizeof f);
cin >> n >> m;
while(m--){
int st,last;
cin >> st >> last;
v[st].push_back({last});
}
int ans=0;
cout << dfs(1,0);
return 0;
}
纯dp
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
struct aaa{
int st,len;
}a[N];
int f[N];//f[i]表示从i-n的休息时间数
int sum,n;
vector<int>v[N];
int main(){
cin >> sum >> n;
for(int i=1;i<=n;i++){
cin >> a[i].st >> a[i].len;
v[a[i].st].push_back(i);
}
for(int i=sum;i>=1;i--){
if(v[i].size()>0){//如果说这个任务点有任务的话
for(int j=0;j<v[i].size();j++){
f[i]=max(f[i],f[i+a[v[i][j]].len]);
}
}
else{//若果没有就加一了
f[i]=f[i+1]+1;
}
}
cout <<f[1];
return 0;
}
五倍经验日
记忆化搜索
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
long long int f[N][N];
bool st[N][N];
long long int a[N];
long long int lose[N],win[N];
long long int use[N];
int n,sum;
long long int dp(int i,int j){
if(i>n) return 0;
if(st[i][j]) return f[i][j];
st[i][j]=true;
if(j>=use[i]){
f[i][j]=max(dp(i+1,j)+lose[i],dp(i+1,j-use[i])+win[i]);
}
else {
f[i][j]=dp(i+1,j)+lose[i];
}
return f[i][j];
}
int main(){
cin >> n >> sum;
for(int i=1;i<=n;i++) cin >> lose[i] >> win[i] >> use[i];
cout << dp(1,sum)*5;
return 0;
}
dp
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1010;
int n,x;
int win[N],lose[N],use[N];
int f[N];//f[i][j] 表示前i个人且做多用j瓶药时所能获得的最大经验值
//状态转移:要么选择用药过,要么不选择用药过
//选择用药 则 f[i][j]=f[i-1][j-use]+win
//不选择用药 f[i][j]=f[i-1][j]+lose
signed main(){
cin >> n >> x;
for(int i=1;i<=n;i++){
cin >> lose[i] >> win[i] >> use[i];
}
for(int i=1;i<=n;i++){
for(int j=x;j>=0;j--){
if(use[i]<=j){
f[j]=max(f[j-use[i]]+win[i],f[j]+lose[i]);
}
else {
f[j]=f[j]+lose[i];
}
}
}
cout << f[x]*5;
return 0;
}
过河卒
记忆化搜索
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
long long int a[N][N];
long long int f[N][N];
bool st[N][N];
long long int endx,endy,mx,may;
long long int dfs(int x,int y){
if(x>endx||y>endy) return 0;
if(st[x][y]) return f[x][y];
if(x==endx&&y==endy){
return 1; //方案数是逐渐被叠加的,这种记忆化搜索方式当然是合理的
}
if(a[x+1][y]!=-1){
f[x][y]=dfs(x+1,y);
}
if(a[x][y+1]!=-1){
f[x][y]+=dfs(x,y+1);
}
st[x][y]=true;
return f[x][y];
}
int main(){
cin >> endx >> endy >> mx >> may;
endx=endx+2;
endy=endy+2;
a[mx+2][may+2]=-1;
a[mx-2+2][may-1+2]=-1;
a[mx-2+2][may+1+2]=-1;
a[mx-1+2][may-2+2]=-1;
a[mx-1+2][may+2+2]=-1;
a[mx+1+2][may-2+2]=-1;
a[mx+1+2][may+2+2]=-1;
a[mx+2+2][may-1+2]=-1;
a[mx+2+2][may+1+2]=-1;
cout << dfs(2,2);
return 0;
}
dp
#include<bits/stdc++.h>
#define int long long
using namespace std;
int vis[25][25];
int dp[25][25]; //表示从(2,2)走到(i,j)的总方案数
signed main(){
dp[2][2]=1;//初始化dp数组,从 (2,2)走到(2,2)的方案数是1;
int n,m,x,y;
cin >> n >> m >> x >> y;
n+=2,m+=2,x+=2,y+=2;//为了防止越界,给所有坐标加上一个偏移量
vis[x][y]=1;
vis[x-2][y-1]=1;
vis[x-2][y+1]=1;
vis[x+2][y-1]=1;
vis[x+2][y+1]=1;
vis[x-1][y+2]=1;
vis[x-1][y-2]=1;
vis[x+1][y+2]=1;
vis[x+1][y-2]=1;
for(int i=2;i<=n;i++){
for(int j=2;j<=m;j++){
if(i==2&&j==2){
continue;
}
if(!vis[i][j]){
dp[i][j]=dp[i-1][j]+dp[i][j-1]; //类似于数字三角形
}
}
}
cout << dp[n][m];
return 0;
}
挖地雷
记忆化搜索
#include<bits/stdc++.h>
using namespace std;
const int N=25;
int n;
bool st[N];
vector<int>a[N];//每个地窖与其他地窖的连接情况
int pre[N];
int num[N]; //每个地窖的地雷数
int f[N];//f[i] 表示从 f[i] 开始挖可以挖到的最多地雷数量
int dfs(int x){
if(st[x]){
return f[x];
}
for(int j=0;j<a[x].size();j++){
int sb=dfs(a[x][j])+num[a[x][j]];
if(f[x]<sb){
f[x]=sb;
pre[x]=a[x][j];
}
}
return f[x];
}
int main(){
cin >> n;
for(int i=1;i<=n;i++){
cin >> num[i];
// f[i]=num[i];
}
int c=0;
for(int i=n-1;i>=1;i--){
c++;
int d=c;
for(int j=1;j<=i;j++){
int flag;
cin >> flag;
if(flag){
a[n-i].push_back({d+1});
}
d++;
}
}
for(int i=1;i<=n;i++){
dfs(i);
st[i]=true;
}
// for(int i=1;i<=n-1;i++){
// for(int j=0;j<a[i].size();j++){
// cout << a[i][j]<<" ";
// }
// cout << endl;
// }
int ans=0;
int res=0;
for(int i=1;i<=n;i++){
if(ans<(f[i]+num[i])){
ans=f[i]+num[i];
res=i;
}
}
cout << res<<" ";
while(pre[res]!=0){
cout << pre[res]<<" ";
res=pre[res];
}
cout << endl;
cout << ans;
return 0;
}
最大食物链计数
记忆化搜索
#include<bits/stdc++.h>
using namespace std;
const int N=5010;
const int mod=80112002;
int n,m;
int f[N]; // f[i] 表示从i开始到弱者有几条食物链
bool st[N];
bool q[N],r[N];
vector<int>v[N];
int sum;
int dfs(int x){
int flag=1;
if(!q[x]){
return 1;
}
if(st[x]){
return f[x];
}
for(int i=0;i<v[x].size();i++){
f[x]=(f[x]+dfs(v[x][i]))%mod;
flag=0;
}
if(flag){
return 0;
}
st[x]=true;
return f[x];
}
int main(){
cin >> n >> m;
for(int i=1;i<=m;i++){
int a,b;
cin >> a >> b;
v[b].push_back({a});
q[b]=true;//强者名单,不在强者名单里的就是弱者qaq
r[a]=true;//弱者名单,不在弱者名单里的就是强者
}
for(int i=n;i>=1;i--){
if(!r[i]){
sum=(sum+dfs(i))%mod;
}
}
cout << sum;
return 0;
}
大师
#include <bits/stdc++.h>
using namespace std;
int n;
const int N=1010,V=40005;
const int vik=20000;
const long long mod=998244353;
int dp[N][V];
int a[N];
void Input(void)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
return ;
}
void solve(void)
{
long long tot=0;
for(int i=1;i<=n;i++)
{
tot++;//这里的=1是把i单独作为一个等差数列加的1
for(int j=1;j<i;j++)
{
dp[i][a[i]-a[j]+vik]=(dp[i][a[i]-a[j]+vik]+dp[j][a[i]-a[j]+vik]+1)%mod;//这里的+1是把i和j单独作为一个等差数列+1
tot=(tot+dp[j][a[i]-a[j]+vik]+1)%mod; //因为公差可能会重复出现 所以这里,tot要直接加上每一次的转移值,比如进行第二重循环时,dp[5][7]可能会出现两次甚至更多,
//为了避免重复加,所以要只加每一次的偏移量即可,实在不理解,可以手动模拟一下 不然要ipad干啥?
}
}
cout<<tot;
}
int main(void)
{
Input();
solve() ;
return 0;
}
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1010;
const int v=20010;//防止数组越界 加一个偏移量
const int mod= 998244353;
int a[N];
int n;
int f[N][v*2];// f[i][j] 表示以第i个数为结尾且公差为k的所有子序列的个数
signed main(){
int n;
cin >> n;
for(int i=1;i<=n;i++){
cin >> a[i];
}
int res=0;
for(int i=1;i<=n;i++){
res++;
for(int j=1;j<i;j++){
f[i][a[i]-a[j]+v]=(f[i][a[i]-a[j]+v]+f[j][a[i]-a[j]+v]+1)%mod;
res=(res+f[j][a[i]-a[j]+v]+1)%mod;
}
}
cout << res;
return 0;
}
参考:来源1
导弹拦截
//第一问求最长不下降子序列 ,第二问 用到一个定理
//一个序列可以拆成最少n个最长不下降子序列的个数=这个序列LIS的长度
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int f[N],p[N];
int a[N],b[N];
int main(){
int i=0;
while(cin >> a[i]){
i++;
}
for(int j=i-1,k=0;j>=0;j--,k++){
b[k]=a[j];
}
int len=0;
for(int j=0;j<i;j++){
int l=0,r=len;
while(l<r){
int mid = l + r + 1 >> 1;
if(f[mid]<=b[j]){
l=mid;
}
else {
r=mid-1;
}
}
len=max(len,r+1);
f[r+1]=b[j];
}
cout << len << endl;
len=0;
int n=i;
for(i=0;i<n;i++){
int l=0,r=len;
while(l < r ){
int mid= l+r+1 >> 1;
if(p[mid]<a[i]){ //我们要找的是小于它的最大值
l=mid;
}
else {
r=mid-1;
}
}
len=max(len,r+1);
p[r+1]=a[i];
}
cout << len <<endl;
return 0;
}
最长公共子序列洛谷版本
//利用全排列性质 nlogn做法
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],b[N];
int mp[N];
int f[N];
int n;
int main(){
cin >> n;
for(int i=0;i<n;i++){
cin >> a[i];
mp[a[i]]=i;
}
for(int i=0;i<n;i++){
cin >> b[i];
}
for(int i=0;i<n;i++){
b[i]=mp[b[i]];
}
int len;
for(int i=0;i<n;i++){
int l=0,r=len;
while(l<r){
int mid = l+r+1 >>1;
if(f[mid]<b[i]){
l=mid;
}
else {
r=mid-1;
}
}
len=max(len,r+1);
f[r+1]=b[i];
}
cout << len;
return 0;
}
鸣人的影分身
经典整数划分问题,状态转移不太好想,根据每个方案是否含0来进行划分
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin >> t;
while(t--){
int n,m;
cin >> m >> n;
int f[21][21];
memset(f,0,sizeof f);
f[0][0]=1;
for(int i=0;i<=m;i++){
for(int j=1;j<=n;j++){
f[i][j]=f[i][j-1];
if(i>=j){
f[i][j]+=f[i-j][j];
}
}
}
cout << f[m][n] << endl;
}
return 0;
}
方格取数
//数字三角形->摘花生 拓展
#include<bits/stdc++.h>
using namespace std;
const int N=15;
int f[N*2][N][N]; //第一维代表行列之和 f[k][i][j] 表示当前的行列之和为k 第一条路径走到第i行
//第二条路径走到第j行 ,两条路径的行列之和是可以公用的
int g[N][N];
int n,a,b,c;
int main(){
cin >> n;
while(cin >> a >> b >> c,a||b||c) g[a][b]=c;
for(int k=2;k<=2*n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int c1=k-i,c2=k-j;
int t=g[i][c1];
if(i!=j){
t+=g[j][c2];
}
if(c1>=1&&c1<=n&&c2>=1&&c2<=n){//防止越界 ,比如当枚举到二人同时走3步时即是k=3时,j可能已经枚举到6此时就不满足状态表示所赋予的含义
f[k][i][j]=max(max(f[k-1][i-1][j],f[k-1][i][j-1]),max(f[k-1][i][j],f[k-1][i-1][j-1]))+t;
}
}
}
}
cout << f[2*n][n][n];
return 0;
}
可以参考:优质题解