首页 > 技术文章 > P7916 [CSP-S 2021] 交通规划

Point-King 2021-11-12 19:38 原文

考虑平面图转对偶图之后,实际上就是一个求最短路的过程,但是这只能解决 \(k=2\) 的问题。

我们考虑当 \(k>2\) 的时候怎么做?

发现如果我们割出了一个最小割,实际上就是将整个问题划分成两个子问题(虽然不再是环了)。对于两个子问题,我们依旧使用同样的方法处理,发现实质上就是一个区间 \(\text{dp}\) 的过程。

所以我们一开始暴力处理出所有颜色分界点两两之间的割,然后跑一个区间 \(\text{dp}\) 就行了?

感觉这个平面图转对偶图非常阴间啊。

我个人的写法感觉是蛮不错的。

#include<bits/stdc++.h>
using namespace std;
const int N=5e2+5,K=55;
const int INF=1e9+7;
int n,m,k,T;
struct Fuck{int w,p,x;}a[K];
bool cmp(Fuck a,Fuck b){return a.p<b.p;}
int id[N][N],tot=0;
int val1[N][N],val2[N][N],val3[N*4];
struct Edge{int nxt,to,val;}e[N*N*4];
int cur[N*N],fir[N*N],e_size,E_size;
void add(int u,int v,int w){
	e[++e_size]=(Edge){cur[u],v,w},cur[u]=e_size;
}
void Add(int u,int v,int w){
	e[++E_size]=(Edge){fir[u],v,w},fir[u]=E_size;
}
int bag[K*2],cnt=0;
int f[K*2][K*2],dis[K][N*N];
struct Data{int dis,u;};
bool operator > (Data a,Data b){return a.dis>b.dis;}
priority_queue<Data,vector<Data>,greater<Data> > q;
void dijkstra(int from,int tag){
	for(int i=1;i<=tot;++i) dis[tag][i]=INF;
	dis[tag][from]=0,q.push((Data){0,from});
	while(!q.empty()){
		Data u=q.top();q.pop();
		if(u.dis>dis[tag][u.u]) continue;
		for(int i=fir[u.u];i;i=e[i].nxt){
			int v=e[i].to;if(dis[tag][v]<=u.dis+e[i].val) continue;
			dis[tag][v]=u.dis+e[i].val,q.push((Data){dis[tag][v],v});
		}
	}
}
int main(){
	cin>>n>>m>>T;
	for(int i=1;i<n;++i){
		for(int j=1;j<=m;++j)
		scanf("%d",&val1[i][j]);
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<m;++j)
		scanf("%d",&val2[i][j]);
	}
	for(int i=0;i<=n;++i){
		for(int j=0;j<=m;++j)
		id[i][j]=++tot;
	}
	for(int i=1;i<n;++i){
		for(int j=1;j<=m;++j){
			add(id[i][j-1],id[i][j],val1[i][j]);
			add(id[i][j],id[i][j-1],val1[i][j]);
		}
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<m;++j){
			add(id[i-1][j],id[i][j],val2[i][j]);
			add(id[i][j],id[i-1][j],val2[i][j]);
		}
	}
	while(T--){
		cin>>k,E_size=e_size;
		for(int i=1;i<=tot;++i) fir[i]=cur[i];
		for(int i=1;i<=2*(n+m);++i) val3[i]=0;
		for(int i=1;i<=k;++i){
			scanf("%d%d%d",&a[i].w,&a[i].p,&a[i].x);
			val3[a[i].p]=a[i].w;
		}
		for(int i=1;i<=2*(n+m);++i){
			if(1<=i&&i<=m){
				Add(id[0][i-1],id[0][i],val3[i]);
				Add(id[0][i],id[0][i-1],val3[i]);
			}
			if(m+1<=i&&i<=m+n){
				Add(id[i-m-1][m],id[i-m][m],val3[i]);
				Add(id[i-m][m],id[i-m-1][m],val3[i]);
			}
			if(m+n+1<=i&&i<=2*m+n){
				Add(id[n][2*m+n-i],id[n][2*m+n-i+1],val3[i]);
				Add(id[n][2*m+n-i+1],id[n][2*m+n-i],val3[i]);
			}
			if(2*m+n+1<=i&&i<=2*(m+n)){
				Add(id[2*(m+n)-i][0],id[2*(m+n)-i+1][0],val3[i]);
				Add(id[2*(m+n)-i+1][0],id[2*(m+n)-i][0],val3[i]);
			}
		}
		sort(a+1,a+1+k,cmp),cnt=0;
		for(int i=1;i<k;++i){
			if(a[i].x!=a[i+1].x){
				if(1<=a[i].p&&a[i].p<=m) bag[++cnt]=id[0][a[i].p];
				if(m+1<=a[i].p&&a[i].p<=m+n) bag[++cnt]=id[a[i].p-m][m];
				if(m+n+1<=a[i].p&&a[i].p<=2*m+n) bag[++cnt]=id[n][2*m+n-a[i].p];
				if(2*m+n+1<=a[i].p&&a[i].p<=2*(n+m)) bag[++cnt]=id[2*(m+n)-a[i].p][0];
			}
		}
		if(a[k].x!=a[1].x){
			if(1<=a[k].p&&a[k].p<=m) bag[++cnt]=id[0][a[k].p];
			if(m+1<=a[k].p&&a[k].p<=m+n) bag[++cnt]=id[a[k].p-m][m];
			if(m+n+1<=a[k].p&&a[k].p<=2*m+n) bag[++cnt]=id[n][2*m+n-a[k].p];
			if(2*m+n+1<=a[k].p&&a[k].p<=2*(n+m)) bag[++cnt]=id[2*(m+n)-a[k].p][0];
		}
		if(!cnt){printf("0\n");continue;}
		for(int i=1;i<=cnt;++i) dijkstra(bag[i],i);
		for(int i=1;i<=cnt;++i) bag[i+cnt]=bag[i];
		for(int i=1;i<=cnt*2;++i){
			for(int j=i;j<=cnt*2;++j) f[i][j]=INF;
			f[i][i+1]=dis[(i-1)%cnt+1][bag[i+1]];
		}
		for(int len=4;len<=cnt;len+=2){
			for(int i=1,j=len;j<=cnt*2;++i,++j){
				for(int c=i+1;c<j;c+=2)
				f[i][j]=min(f[i][j],f[i][c]+f[c+1][j]);
				f[i][j]=min(f[i][j],f[i+1][j-1]+dis[(i-1)%cnt+1][bag[j]]);
			}
		}
		int res=INF;
		for(int i=1;i<=cnt;++i) res=min(res,f[i][i+cnt-1]);
		printf("%d\n",res);
	}
}

推荐阅读