滑雪
dfs,然后再记忆化搜索。因为遍历到一个点之后,遍历上下左右四种情况,把最好的算出来保存,下次就不用算了。
#include "bits/stdc++.h"
using namespace std;
#define maxn 120
#define inf 1234567890
int a[maxn][maxn],ans[maxn][maxn],r,c;
int dix[5]={0,0,-1,1};
int diy[5]={-1,1,0,0};
int dfs(int x,int y){
if(ans[x][y]){
return ans[x][y];
}
ans[x][y]=1;
for(int i=0;i<4;i++){
int xx=x+ dix[i];
int yy=y+diy[i];
if(xx>=1&&xx<=r&&yy>=1&&yy<=c&&a[xx][yy]<a[x][y]){
ans[x][y]=max(ans[x][y],dfs(xx,yy)+1);
}
}
return ans[x][y];
}
int main(){
cin>>r>>c;
for(int i=1;i<=r;i++){
for(int j=1;j<=c;j++){
cin>>a[i][j];
}
}
int max_ans=0;
for(int i=1;i<=r;i++){
for(int j=1;j<=c;j++){
max_ans=max(max_ans,dfs(i,j));
}
}
cout<<max_ans;
return 0;
}
挖地雷
本质上就是最长递增子序列。
#include "bits/stdc++.h"
using namespace std;
int head[25],a[25],n,v[25][25],ans[25];
stack<int> s;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
ans[i]=a[i];
}
for(int i=1;i<=n-1;i++){
for(int j=1;j<=n-i;j++){
cin>>v[i][i+j];
// v[i+j][i]=v[i][i+j];
}
}
head[1]=1;
for(int i=2;i<=n;i++){
int maxn=0,k;
for(int j=1;j<i;j++){
if(v[j][i]){
if(maxn<ans[j]){
maxn=ans[j];
k=j;
}
}
}
ans[i]=maxn+a[i];
if(maxn==0){
head[i]=i;
}else{
head[i]=k;
}
}
int res=0,resx=0;
for(int i=1;i<=n;i++){
if(ans[i]>res){
res=ans[i];
resx=i;
}
}
int i;
for(i=resx;head[i]!=i;i=head[i]){
s.push(i);
}
s.push(i);
while(!s.empty()){
cout<<s.top()<<" ";
s.pop();
}
cout<<endl;
cout<<res;
return 0;
}
最长食物链计数
一开始会错意了,以为和上题一样计算最长的食物链有多长。但它的意思是计算最长的食物链有多少条。何为最长食物链,起点是最佳生产者,终点是最佳消费者,这样的食物链有多少条。因为要判断这两者,所以需要记录入度和出度。计算到达最佳消费者的路径有多少条,是由能够到达它的前面结点所决定,即前面结点的路径数之和,而前面路径又由更前面的结点决定,因此最终追溯到最佳生产者。从起点出发,可以到达的结点加1,入度减1,将入度为0的点加入队列,删除结点,以此类推,就联想到拓扑排序。
拓扑排序就是两个结点间一定有一条路径,并且前一结点在图中一定在后一结点前面。
比如计算机专业应修课程,遍历入度为0的点,就是第一学期的课程,不需要任何基础,然后遍历队列中的点,就要修了先修课才能上,这样形成一个序列。
#include "bits/stdc++.h"
using namespace std;
#define ll long long
#define maxl 5005
int n,v[maxl][maxl],ru[maxl],chu[maxl],m,ans,a,b,f[maxl];
queue<int> q;
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>a>>b;
v[a][b]=1;
chu[a]++;
ru[b]++;
}
for(int i=1;i<=n;i++){
if(ru[i]==0){
f[i]=1;
q.push(i);
}
}
while(!q.empty()){
int a=q.front();
q.pop();
for(int k=1;k<=n;k++){
if(v[a][k]==0){
continue;
}
f[k]+=f[a];
f[k]%=80112002;
ru[k]--;
if(ru[k]==0){
if(chu[k]==0){
ans+=f[k];
ans%=80112002;
}
q.push(k);
}
}
}
cout<<ans;
return 0;
}
采药
最普通的01背包问题
以前解背包问题都是用二维数组。
#include "bits/stdc++.h"
using namespace std;
#define maxn 1005
int t,m,weigth[105],value[105],a[105][maxn];
int main(){
cin>>t>>m;
for(int i=1;i<=m;i++){
cin>>weigth[i]>>value[i];
}
for(int i=1;i<=m;i++){
for(int j=1;j<=t;j++){
if(weigth[i]<=j){
a[i][j]= max(a[i-1][j],
a[i-1][j-weigth[i]]+value[i]);
}else{
a[i][j]=a[i-1][j];
}
}
}
cout<<a[m][t];
return 0;
}
这次发现完全可以用滚动数组来节省空间,但是一维数组需要注意第二层循环是从后往前的,因为01背包不能只能加一次,这也是与下面完全背包问题的区别。
#include "bits/stdc++.h"
using namespace std;
int w[105], val[105];
int dp[1005];
int main()
{
int t,m,res=-1;
scanf("%d%d",&t,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&w[i],&val[i]);
}
for(int i=1;i<=m;i++)
{
for(int j=t;j>=0;j--)
{
if(j>=w[i])
{
dp[j]=max(dp[j-w[i]]+val[i], dp[j]);
}
}
}
printf("%d",dp[t]);
return 0;
}
疯狂的采药
以前专业课学习的时候都是用二维数组,也没考虑优化什么的,这次用二维数组直接超出空间,只能用滚动数组。因为完全背包可以被重复添加,所以从前往后遍历。原来二维数组的k,表示可以买一件或多件,这里不需要担心w[i]是否需要整数倍添加,因为w[i]-2*w[i]之间的值,结果和w[i]的值一样。
#include "bits/stdc++.h"
using namespace std;
#define maxn 10005
#define maxt 10000005
#define ll long long
ll t,m,value[maxn],weigth[maxn], a[maxt];
int main(){
cin>>t>>m;
for(int i=1;i<=m;i++){
cin>>weigth[i]>>value[i];
}
for(int i=1;i<=m;i++){
for(int j=weigth[i];j<=t;j++){
a[j]= max(a[j],a[j-weigth[i]]+value[i]);
}
}
cout<<a[t];
return 0;
}
5 倍经验日
#include "bits/stdc++.h"
using namespace std;
#define maxn 1005
int n,x,lose[maxn],win[maxn],w[maxn],a[maxn][maxn],dp[maxn];
int main(){
cin>>n>>x;
for(int i=1;i<=n;i++){
cin>>lose[i]>>win[i]>>w[i];
}
for(int i=1;i<=n;i++){
// 当药物为0的时候,也需要加上lose的值,之前错就在这地方
for(int j=0;j<=x;j++){
if(w[i]<=j){
a[i][j]= max(a[i-1][j]+lose[i],
a[i-1][j-w[i]]+win[i]);
}else{
a[i][j]=a[i-1][j]+lose[i];
}
}
}
// for(int i=1;i<=n;i++){
// for(int j=x;j>=0;j--){
// if(w[i]<=j){
// dp[j]= max(dp[j]+lose[i],
// dp[j-w[i]]+win[i]);
// }else{
// dp[j]=dp[j]+lose[i];
// }
// }
// }
printf("%lld",5ll*a[n][x]);
return 0;
}