20221012

20221011

理想的正方形

传送门

单调队列

​ 显然可以对一个\(n\times n\)的区间的最值进行维护,但直接维护最值无法及时让不在范围内的值弹出,并消去它的影响,而对于这种需要将不在范围内的弹出的同时维护最值的,我们自然而然就想到了单调队列。但是单调队列貌似只能维护一维的数值,而这里却需要维护二维的数值,那该怎么办呢?既然单调队列只能维护一维的数值,那我们先维护横行的一维,再维护竖列的一维不就能维护两维了吗?两个单调队列分别维护最大和最小值,再用四个数组存下横行的最值信息和竖列的最值信息,最后统计答案即可。

点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
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);
}
const int N=1005,NN=1e6+5;
int a,b,n;
int val[N][N];
int ans;
int X[N][N],x[N][N],Y[N][N],y[N][N];
//X维护行最大值,x维护行最小值,Y维护列最大值,y维护列最小值
int Q[N],q[N],Head,head,Tail,tail;
//Q维护最大值的单调队列,q维护最小值的单调队列
inline int maX(int a,int b){return a>b?a:b;}
inline int miN(int a,int b){return a<b?a:b;}
int main(){
    in(a),in(b),in(n);
    fo(i,1,a)fo(j,1,b)in(val[i][j]);
    fo(i,1,a){
        head=Head=0;
        tail=Tail=Q[1]=q[1]=1;
        fo(j,2,b){
            while(val[i][j]>=val[i][Q[Tail]]&&Tail>Head)--Tail;
            while(val[i][j]<=val[i][q[tail]]&&tail>head)--tail;
            Q[++Tail]=j,q[++tail]=j;
            while(j-Q[Head+1]>=n)++Head;
            while(j-q[head+1]>=n)++head;
            if(j>=n)X[i][j-n+1]=val[i][Q[Head+1]];
            if(j>=n)x[i][j-n+1]=val[i][q[head+1]];
        }
    }
    fo(i,1,b-n+1){
        head=Head=0;
        tail=Tail=Q[1]=q[1]=1;
        fo(j,2,a){
            while(X[j][i]>=X[Q[Tail]][i]&&Tail>Head)--Tail;
            while(x[j][i]<=x[q[tail]][i]&&tail>head)--tail;
            Q[++Tail]=j,q[++tail]=j;
            while(j-Q[Head+1]>=n)++Head;
            while(j-q[head+1]>=n)++head;
            if(j>=n)Y[j-n+1][i]=X[Q[Head+1]][i];
            if(j>=n)y[j-n+1][i]=x[q[head+1]][i];
        }
    }
    ans=0x3f3f3f3f;
    fo(i,1,a-n+1)
        fo(j,1,b-n+1)
            ans=min(ans,Y[i][j]-y[i][j]);
    out(ans);
    return 0;
}

凸多边形的划分

传送门

区间dp

​ 区间\(dp\)版题,\(dp[i][j]\)表示从顶点\(i\)到顶点\(j\)的顶点权值乘积和的最小值。\(w[i]\)表示顶点\(i\)的权值。

\[dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+w[i]\times w[j]\times w[k])(i\le k\le j) \]

注意爆\(longlong\)
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
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);
}
const int N=505;
int n;
__int128 w[N];
__int128 dp[N][N],ans;
inline __int128 miN(__int128 a,__int128 b){
    return a<b?a:b;
}
int main(){
    in(n);
    fo(i,1,n){
        in(w[i]);
        w[i+n]=w[i];
    }
    n<<=1;
    fo(i,1,n){
        fo(j,1,n){
            dp[i][j]=(__int128)1e30;
        }
    }
    fo(i,1,n)dp[i][i+1]=0;
    n>>=1;
    ans=(__int128)1e30;
    fo(len,2,n-1){
        fo(j,1,n){
            fo(k,j+1,j+len-1){
                dp[j][j+len]=miN(dp[j][j+len],dp[j][k]+dp[k][j+len]+w[j]*w[j+len]*w[k]);
            }
        }
    }
    fo(i,1,n)ans=miN(ans,dp[i][i+n-1]);
    out(ans);
    return 0;
}

电路维修

传送门

题目分析

​ 对于每一条线路,只有两种存在方式,要么\要么/,转化一次状态对答案的贡献就为\(1\),而我们实际上要求的就是从左上角走到右下角的最小状态改变次数。所以我们只需要建边后跑一遍最短路即可。那么怎么建边呢?如果一条线路为\那么就建立一条从左上的点到右下的点的一条边权为\(0\)的边,再建立一条从右上到左下的边权为\(1\)的边,/反之。

点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
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);
}
const int N=505;
int n,m;
char map[N][N];
struct edge{
    int v,w,nex;
}e[1000005];
int cnt;
int head[1000005],tot,d[1000005];
bool vis[1000005];
int en;
inline void init(){
    fo(i,1,en){
	vis[i]=head[i]=0;
	d[i]=0x3f3f3f3f;
    }
    tot=cnt=0;
}
inline void add(int u,int v,int w){
    e[++tot].v=v,e[tot].w=w,e[tot].nex=head[u],head[u]=tot;
    e[++tot].v=u,e[tot].w=w,e[tot].nex=head[v],head[v]=tot;
}
priority_queue < pair<int,int> >q;
inline void dj(){
    d[1]=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(d[v]>d[x]+w){
		d[v]=d[x]+w;
		q.push(make_pair(-d[v],v));
	    }
	}
    }	
}
int main(){
    int t;
    in(t);
    while(t--){
	in(n),in(m);
	en=(m+1)*(n+1);
	init();
	fo(i,1,n){
	    fo(j,1,m){
		map[i][j]=getchar();
		++cnt;
		add(cnt,cnt+m+2,map[i][j]=='/'?1:0);
		add(cnt+1,cnt+m+1,map[i][j]=='/'?0:1);
	    }
	    ++cnt;
	    getchar();
	}
	dj();
	if(!vis[en]){
	    puts("NO SOLUTION");
	    continue;
	}
	out(d[en]),putchar('\n');
    }
    return 0;
}

换教室

传送门

题目分析

​ 因为每节课是按顺序上的,又有换或不换两种状态,最多又只能选\(m\)节课换,很容易想到\(dp\)。定义\(dp[i][j][0/1]\)为到第\(i\)个教室,选择换了\(j\)节课,当前这节课换或不换的耗费体力值的最小期望值。当然,每次选择换课还有换成功和失败两种状态,要各自乘上他们对应的期望。然后还需要预处理出从\(i\)教室到\(j\)教室的最短距离\(dis[i][j]\),这里可以用\(floyd\)来完成。

\[dp[i][j][0]=min(dp[i-1][j][1]+dis[c[i]][d[i-1]]*k[i-1]+dis[c[i]][c[i-1]]*(1-k[i-1]),dp[i-1][j][0]+dis[c[i]][c[i-1]]); \]

\[dp[i][j][1]=min(dp[i-1][j-1][1]+dis[d[i-1]][d[i]]*k[i]*k[i-1]+dis[d[i-1]][c[i]]*k[i-1]*(1-k[i])+dis[c[i-1]][d[i]]*(1-k[i-1])*k[i]+dis[c[i-1]][c[i]]*(1-k[i-1])*(1-k[i]),dp[i-1][j-1][0]+dis[c[i-1]][d[i]]*k[i]+dis[c[i-1]][c[i]]*(1-k[i])); \]

点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
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);
}
const int N=2005;
int n,m,v,e;
int c[N],d[N];
double k[N];
int dis[N][N];
double dp[N][N][2];
int main(){
    in(n),in(m),in(v),in(e);
    fo(i,1,n)in(c[i]);
    fo(i,1,n)in(d[i]);
    fo(i,1,n)cin>>k[i];
    int a,b,w;
    memset(dis,0x3f,sizeof(dis));
    fo(i,1,e){
	in(a),in(b),in(w);
	dis[a][b]=dis[b][a]=min(dis[a][b],w);
    }
    fo(k,1,v)
	fo(i,1,v)
	    fo(j,1,i-1)
		dis[j][i]=dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    fo(i,1,v)dis[i][i]=0;
    fo(i,0,n){
	fo(j,0,m){
	    dp[i][j][0]=dp[i][j][1]=1e9;
	}
    }
    dp[1][0][0]=dp[1][1][1]=0;
    fo(i,2,n){
	fo(j,0,min(i,m)){
	    dp[i][j][0]=min(dp[i-1][j][1]+dis[c[i]][d[i-1]]*k[i-1]+dis[c[i]][c[i-1]]*(1-k[i-1]),dp[i-1][j][0]+dis[c[i]][c[i-1]]);
	    if(j!=0)dp[i][j][1]=min(dp[i-1][j-1][1]+dis[d[i-1]][d[i]]*k[i]*k[i-1]+dis[d[i-1]][c[i]]*k[i-1]*(1-k[i])+dis[c[i-1]][d[i]]*(1-k[i-1])*k[i]+dis[c[i-1]][c[i]]*(1-k[i-1])*(1-k[i]),dp[i-1][j-1][0]+dis[c[i-1]][d[i]]*k[i]+dis[c[i-1]][c[i]]*(1-k[i]));
	}
    }
    double ans=1e9;
    fo(i,0,m)
	ans=min(ans,min(dp[n][i][0],dp[n][i][1]));
    printf("%.2lf",ans);
    return 0;
}
posted @ 2022-10-12 17:10  リン・グァン  阅读(22)  评论(0编辑  收藏  举报