关于我好不容易写的题解因为关机而无了这件事
本来写了挺多的,现在不想多说了,简单来说就是维护一个二维的单调队列
就是对每一行维护从开始长度为的区间中的最大最小值
对我们一维单调队列维护出来所有处于同一列的值,纵向维护最大最小值,就可以得到一个矩形内的最大最小值了
| #include<bits/stdc++.h> |
| using namespace std; |
| const int maxn=1e3+100; |
| int maxh[maxn][maxn],minh[maxn][maxn]; |
| int Maxh[maxn][maxn],Minh[maxn][maxn]; |
| int v[maxn][maxn]; |
| int a,b,n,qmax[maxn],qmin[maxn]; |
| int H,T,h,t; |
| int main() |
| { |
| scanf("%d%d%d",&a,&b,&n); |
| for(register int i=1;i<=a;++i) |
| for(register int j=1;j<=b;++j) |
| scanf("%d",&v[i][j]); |
| |
| for(register int i=1;i<=a;++i) |
| { |
| H=T=h=t=qmax[1]=qmin[1]=1; |
| for(register int j=2;j<=b;++j) |
| { |
| while(H<=T&&v[i][j]>=v[i][qmax[T]])T--; |
| while(h<=t&&v[i][j]<=v[i][qmin[t]])t--; |
| qmax[++T]=qmin[++t]=j; |
| while(j-qmax[H]>=n)H++; |
| while(j-qmin[h]>=n)h++; |
| if(j>=n) |
| maxh[i][j-n+1]=v[i][qmax[H]], |
| minh[i][j-n+1]=v[i][qmin[h]]; |
| } |
| } |
| for(register int j=1;j<=b-n+1;++j) |
| { |
| H=T=h=t=qmax[1]=qmin[1]=1; |
| for(register int i=2;i<=a;++i) |
| { |
| while(H<=T&&maxh[i][j]>=maxh[qmax[T]][j])T--; |
| while(h<=t&&minh[i][j]<=minh[qmin[t]][j])t--; |
| qmax[++T]=qmin[++t]=i; |
| while(i-qmax[H]>=n)H++; |
| while(i-qmin[h]>=n)h++; |
| if(i>=n) |
| Maxh[i-n+1][j]=maxh[qmax[H]][j], |
| Minh[i-n+1][j]=minh[qmin[h]][j]; |
| } |
| } |
| int ans=2005120700; |
| for(register int i=1;i+n-1<=a;++i) |
| for(register int j=1;j+n-1<=b;++j) |
| ans=min(ans,Maxh[i][j]-Minh[i][j]); |
| cout<<ans; |
| return 0; |
| } |
不想多说了,我写的是网络流,对每一个点,他可以和所有不和他相邻的边匹配,然后简简单单建模一下,直接跑就行了,但是有问题:会考虑到同一个三角形,即使在图上是完全不同的两组匹配,可能是我太菜了,反正按照我现在的网络流水平,我是无法处理这种情况的。
对于每两个点(记为)的连线,我们考虑从到的子多边形,把它划分成若干个三角形的最小化费,首先值得肯定的是,我们需要把动态规划的问题转化成每一个之前阶段的子问题,也就是说,这个子多边形还要划分成不同的子多边形,那么就是妥妥的区间动态规划问题问题了。
最外层枚举两个端点之间的距离,然后是左端点和中间点,对于我们枚举的每个:的连线都会构成一个三角形,产生新的花费,同时把多边形割成两个子多边形加上中间的三角形,最后按着定义来就是区间的板子了
转移方程是这样的
这个数据范围当然也是无法存下来的,最方便的就是用了
| #include<iostream> |
| #include<cstdio> |
| #include<cstring> |
| #define int __int128 |
| using namespace std; |
| const int maxn=105,INF=1e30; |
| int dp[maxn][maxn],a[maxn]; |
| using namespace std; |
| inline int re() |
| { |
| int x=0,f=1; |
| char c=getchar(); |
| for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; |
| for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^48); |
| return x*=f; |
| } |
| inline void wr(int n) |
| { |
| if(n==0) return; |
| if(n<0)putchar('-'),n=-n; |
| wr(n/10); |
| putchar(n%10+'0'); |
| } |
| inline int min(int x,int y){return x>y?y:x;} |
| signed main() |
| { |
| int n; |
| n=re(); |
| for(register int i=1;i<=n;++i)a[i]=re(); |
| for(register int len=3;len<=n;++len) |
| { |
| for(register int l=1;l+len-1<=n;++l) |
| { |
| int r=l+len-1; |
| dp[l][r]=INF; |
| for(register int k=l+1;k<=r-1;++k) |
| dp[l][r]=min(dp[l][r],dp[l][k]+dp[k][r]+a[l]*a[k]*a[r]); |
| } |
| } |
| wr(dp[1][n]); |
| return 0; |
| } |
可以但是我不会也可以最短路,既然是最小步数,那么理所应当的想到要么就是,要么就是最短路了。
对于给出的,我们把当前格子拆成四个点,把左下角和右上角建一条边权为的双向边,表示不用改动就可以从这两个点互相到达;把左上角和右下角建一条边权为的双向边,表示需要改变一次才能使他们互相到达
对于给出的' \ ',我们做相反的操作就行了,应该很好理解
不用赘述了吧
首先稠密图肯定不能用了,然而这道题普通的的效率是的,所以还需要堆优化,复杂度就可以降到了
| #include<iostream> |
| #include<cstdio> |
| #include<algorithm> |
| #include<cstring> |
| #include<queue> |
| #define int long long |
| using namespace std; |
| const int maxm=1e6; |
| const int maxn=3e5; |
| struct Edge{int u,v,w,nex;}E[maxm]; |
| int T,n,m,s,t,tote,head[maxm]; |
| void add(int u,int v,int w){E[++tote].u=u,E[tote].v=v,E[tote].w=w,E[tote].nex=head[u],head[u]=tote;} |
| int dis[maxm],vis[maxm]; |
| char map[550][550]; |
| template<typename T>inline void in(T &x){ |
| x=0;int f=0;char c=getchar(); |
| for(;!isdigit(c);c=getchar())f|=(c=='-'); |
| for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48); |
| x=f?-x:x; |
| } |
| template<typename T>inline void out(T x){ |
| if(x<0)x=~x+1,putchar('-'); |
| if(x>9)out(x/10); |
| putchar(x%10^48); |
| } |
| void ADD(int x,int y) |
| { |
| if(map[x][y]=='/') |
| { |
| add((m+1)*x+y,(m+1)*(x-1)+y+1,0); |
| add((m+1)*(x-1)+1+y,(m+1)*x+y,0); |
| add((m+1)*(x-1)+y,(m+1)*x+y+1,1); |
| add((m+1)*x+y+1,(m+1)*(x-1)+y,1); |
| } |
| else |
| { |
| add((m+1)*x+y,(m+1)*(x-1)+y+1,1); |
| add((m+1)*(x-1)+1+y,(m+1)*x+y,1); |
| add((m+1)*(x-1)+y,(m+1)*x+y+1,0); |
| add((m+1)*x+y+1,(m+1)*(x-1)+y,0); |
| } |
| } |
| void reset() |
| { |
| memset(dis,0x3f,sizeof dis); |
| memset(vis,0,sizeof vis); |
| memset(head,0,sizeof head); |
| tote=0; |
| } |
| priority_queue < pair<int,int> >q; |
| signed main() |
| { |
| scanf("%lld",&T); |
| while(T--) |
| { |
| reset(); |
| in(n),in(m); |
| for(int i=1;i<=n;++i) |
| { |
| for(int j=1;j<=m;++j) |
| map[i][j]=getchar(),ADD(i,j); |
| getchar(); |
| } |
| s=1,t=(n+1)*(m+1); |
| dis[s]=0; |
| q.push(make_pair(0,1)); |
| while(!q.empty()) |
| { |
| int x=q.top().second;q.pop(); |
| if(vis[x])continue; |
| vis[x]=1; |
| for(int i=head[x];i;i=E[i].nex) |
| { |
| int v=E[i].v,w=E[i].w; |
| if(dis[v]>dis[x]+w) |
| { |
| dis[v]=dis[x]+w; |
| q.push(make_pair(-dis[v],v)); |
| } |
| } |
| } |
| if(!vis[t])printf("NO SOLUTION\n"); |
| else out(dis[t]),putchar('\n'); |
| } |
| |
| return 0; |
| } |
一眼
首先题目中给出的点数是小于的,所以可以直接用预处理出每个点之间的最小距离,然后注意题目中是会给出重边的,甚至会存在自环,所以我们需要特判一下,并且每次输入的边应该和当前的边取最小值才行,然后注意每个点到自己的距离一定是,因为当前这节课和上一节课上课的教室可能是同一间,所以当前点到当前点的最小距离应该是
很容易看出来这道题的阶段是前间教室,然后状态就是用了次换教室的机会,但是这个时候,我们仍然无法涉及到换课与否,所以应该另外引入一维状态表示当前状态是否选择了换课
然后定义出来了转移按照定义就应该很好写了
那么就需要从第个阶段转移过来,此阶段可能申请换课,也可能不会,于是我们把每种可能乘以他的期望
写出来就是
同样需要从第个阶段转移过来,可能换课,也可能不会
写出来是
注意
| #include<bits/stdc++.h> |
| #define re register |
| using namespace std; |
| const int maxn=2e3+100; |
| const double INF=1e18; |
| int N,M;int n,m,u,v,w; |
| int c[maxn],d[maxn];int dis[maxn][maxn]; |
| double k[maxn],dp[maxn][maxn][2]; |
| void floyd() |
| { |
| for(re int k=1;k<=n;++k) |
| for(re int i=1;i<=n;++i) |
| for(re int j=1;j<=n;++j) |
| dis[j][i]=dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); |
| for(re int i=1;i<=n;++i)dis[i][i]=0; |
| } |
| void prework() |
| { |
| scanf("%d%d%d%d",&N,&M,&n,&m); |
| memset(dis,63,sizeof dis); |
| for(re int i=0;i<=N;++i)for(re int j=0;j<=M;++j)dp[i][j][0]=dp[i][j][1]=INF; |
| for(re int i=1;i<=N;++i)scanf("%d",&c[i]); |
| for(re int i=1;i<=N;++i)scanf("%d",&d[i]); |
| for(re int i=1;i<=N;++i)scanf("%lf",&k[i]); |
| for(re int i=1;i<=m;++i) |
| { |
| scanf("%d%d%d",&u,&v,&w); |
| dis[u][v]=dis[v][u]=min(dis[u][v],w); |
| } |
| floyd(); |
| dp[1][0][0]=dp[1][1][1]=0; |
| } |
| int main() |
| { |
| freopen("classroom.in","r",stdin); |
| freopen("classroom.out","w",stdout); |
| prework(); |
| for(re int i=2;i<=N;++i) |
| { |
| dp[i][0][0]=dp[i-1][0][0]+dis[c[i-1]][c[i]]; |
| for(re int j=1;j<=min(i,M);++j) |
| { |
| double tmp1=INF,tmp2; |
| tmp1=min(dp[i-1][j][0] |
| +dis[c[i-1]][c[i]], |
| |
| dp[i-1][j][1] |
| +dis[c[i-1]][c[i]]*(1-k[i-1]) |
| +dis[d[i-1]][c[i]]*k[i-1]); |
| tmp2=min(dp[i-1][j-1][0] |
| +dis[c[i-1]][c[i]]*(1-k[i]) |
| +dis[c[i-1]][d[i]]*k[i], |
| |
| dp[i-1][j-1][1] |
| +dis[c[i-1]][c[i]]*(1-k[i])*(1-k[i-1]) |
| +dis[d[i-1]][c[i]]*(1-k[i])*k[i-1] |
| +dis[c[i-1]][d[i]]*k[i]*(1-k[i-1]) |
| +dis[d[i-1]][d[i]]*k[i]*k[i-1]); |
| dp[i][j][0]=min(dp[i][j][0],tmp1); |
| dp[i][j][1]=min(dp[i][j][1],tmp2); |
| } |
| } |
| double ans=INF; |
| for(re int i=0;i<=M;++i)ans=min(ans,min(dp[N][i][1],dp[N][i][0])); |
| printf("%.2lf",ans); |
| return 0; |
| } |
| |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效