2024/12/7课堂记录
目录
依然是拓扑,比较难
公式可能看不懂,但具体意思是:第i个点的值=∑(所有指向他的点)(这个点的值*这条边的权)-点i的阀值
这坑以后填
填一下2024/11/23的坑,第四题,当时是半个作业
贪心/暴力的代码之前的文章有过,这里不过多说了
这次主要写dp(前缀和)的方法O(n^2*m)
说一下前缀和(压缩):sum[i]=a[1]+a[2]+…+a[i];
a[6]={1,1,1,1,2,4}->sum[6]={1,2,3,4,6,10}
一般的利用:(j>i)求区间和
sum[j]-sum[i]=a[i+1]+a[i+2]+…+a[j-1]+a[j]
如sum[5]-sum[2]=10-3=7
此时i=2;j=5
其实就是求a[3]+a[4]+a[5]=1+2+4=7
针对本题,是这样压缩:
a[][]
1 ,2 ,3 ,4
5 ,6 ,7 ,8
9 ,10,11,12
13,14,15,16
|
\|/
num[][]
1 ,2 ,3 ,4
6 ,8 ,10,12
15,18,21,24
28,32,36,40
利用tmp[]再次压缩:
最后每一排的一维最大连续字段和
再取所有排中最大和的最大值就行
代码没自己写,用的老师的
//O(n^2*m)
//n行m列
#include<iostream>
#include<cstring>
using namespace std;
int a[121][121];
int sum[121][121];
int n,m;
int tmp[121];
int ans=0xafffffff; // 负无穷; 正无穷通常是: 0x3f3f3f3f;
void dp()
{
//枚举一个子矩阵的第1行i和最后1行j
for(int i=0;i<=n;i++) //枚举矩形的上一行
{
for(int j=i+1;j<=n;j++) //枚举矩形的下一行
{
memset(tmp,0,sizeof(tmp));
for(int k=1;k<=m;k++)
{
tmp[k]=sum[j][k]-sum[i][k]; // a[i+1][k]+a[i+2][k]....+a[j][k]
}
//做一维最大连续字段和
int cur=0;//在加当前第i位之前最大字段和;
for(int k=1;k<=m;k++)
{
cur=cur+tmp[k];
if(cur>=ans)
{
ans=cur;
}
if(cur<0)
cur=0;
}
}
}
}
int main()
{
cin>>n; //n行m列
m=n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
sum[i][j]=sum[i-1][j]+a[i][j]; //压缩列
//sum[i][j]: a[1][j]+a[2][2]+a[3][j]....a[i][j]
// 前i行,第j列,所有格子的和
}
}
dp();
cout<<ans<<endl;
}
一道经典dfs,注意分类讨论,再者需要剪枝,其他也就没什么了
直接看代码吧,注释都写了
#include<iostream>
#include<cstring>
using namespace std;
int mapp[110][110],ans=0x3f3f3f3f;
int minn[110][110];
int fx[5]={0,0,-1,0,1};
int fy[5]={0,-1,0,1,0};
int m,n;
void dfs(int x,int y,int money,int color)
{
//剪枝
if(money>=ans)return; //比最终答案大
if(money>=minn[x][y])return; //比最短路径大
//到达终点
if(x==m&&y==m)
{
ans=min(ans,money);
minn[x][y]=ans;
// cout<<ans;
return;
}
minn[x][y]=money;
//开始dfs(没到达终点且是目前最短路)
// cout<<x<<" "<<y<<"\n";
for(int w=1;w<=4;w++)
{
int xx=x+fx[w];
int yy=y+fy[w];
if(xx>=1&&xx<=m&&yy>=1&&yy<=m)
if(mapp[x][y]>=0&&mapp[xx][yy]>=0) //脚下和眼前都有颜色
if(mapp[x][y]==mapp[xx][yy])dfs(xx,yy,money,mapp[xx][yy]); //脚下和眼前颜色相同
else dfs(xx,yy,money+1,mapp[xx][yy]); //脚下和眼前颜色不用
else if(mapp[x][y]>=0&&mapp[xx][yy]<0) //脚下有颜色眼前没有
dfs(xx,yy,money+2,mapp[x][y]); //施展魔法
else if(mapp[x][y]<0&&mapp[xx][yy]>=0) //眼前有颜色脚下没有
dfs(xx,yy,money+((color==mapp[xx][yy])?0:1),mapp[xx][yy]); //脚下是魔法
}
}
int main()
{
cin>>m>>n;
memset(mapp,-1,sizeof(mapp));//-1代表空白
memset(minn,0x3f,sizeof(minn));
while(n--)
{
int x,y,c;
cin>>x>>y>>c;
mapp[x][y]=c;
}
dfs(1,1,0,mapp[1][1]);
if(ans==0x3f3f3f3f)cout<<-1;
else cout<<ans;
}
单调栈
本题让求能看到多少头牛,如果暴力肯定就超时了
换种思路:有多少头牛能看见他
从左往右,挨个入栈,把所有比他小的全部踢出去(保持栈单调下降)
把所有比他小的出栈->把现在栈的长度累加->入栈
以样例一为例
10:不出栈->长度0->10入栈
3:不出栈->长度1->3入栈
7:3出栈->长度1->7入栈
4:不出栈->长度2->4入栈
12:4,7,10出栈->长度0->12入栈
2:不出栈->长度1->2入栈
最终答案:0+1+1+2+0+1=5
最后:不开longlong见祖宗
我觉得解释的挺明白的,不写注释了
#include<iostream>
using namespace std;
int s[100010];
int top;
int main()
{
int n;
long long int ans=0;
cin>>n;
while(n--)
{
int h;
cin>>h;
for(int j=top-1;j>=0;j--)
if(s[j]!=0&&s[j]<=h)top--;
else break;
ans+=top;
s[top++]=h;
}
cout<<ans;
}