test20190416 Posted on 2019-04-17 | Edited on 2019-05-04 | Views: 来自 $GDOI$ 的模拟题? 题面 $graph$ 貌似是第一次在考试中遇到提答题? 这题看上去十分正常,却出成提答,就是让你乱搞的…题解给的做法就是退火之类的乱搞. 随便估价乱搞一下就可以获得 $80+$ 的好成绩? $guess$ 自己的联想发散能力还是有问题,没看出可以建网络流模型,只会打暴力… 正解:要求的就是所有合法配对方案中,出现原来配对情况的最小数目. 先离散化一下数字大小,建一个费用流的模型,从 $S$ 向左边每个数字连其在 $x$ 坐标中出现的次数作为流量, $0$ 作为费用的边. $y$ 坐标连类似的边连向 $T$ .中间对于每个原来有的配对,连一条流量为 $1$ ,费用为 $1$ 的边.这样就限制了不会出现重复的配对. 跑一遍 $mcmf$ 即为答案. $room$ 最开始想到最小割去了..然后发现好像不太现实,暴力转移的 $dp$ 倒是很普及… 设 $f[i][j][k]$ 表示已经走了前 $i$ 层,第 $i$ 层开的门分别为 $j,k$ 时的最小体力花费. 这个东西显然可以 $O(nm^4)$ 大力转移. $f[1][j][k]=t[1][j]+t[1][k]$$f[i][j][k]=\min_{x\not = y} f[i-1][x][y]+K\cdot (|j-x|+|k-y|),2\leq i \leq n.$ 后两维交换是没有影响的,所以在枚举是可以直接钦定 $j<k,x<y$ ,优化了 $16$ 倍常数.这样就有 $60pts$ 了. $100pts?$ 转移时允许在同层转移,形成一个类似前缀 $\min$ 的优化,即可做到 $O(nm^2)$ . 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556#include<bits/stdc++.h>#define rg registerusing namespace std;typedef long long ll;const int inf=1e9;inline int read(){ int out=0,fh=1; char jp=getchar(); while ((jp>'9'||jp<'0')&&jp!='-') jp=getchar(); if (jp=='-') fh=-1,jp=getchar(); while (jp>='0'&&jp<='9') out=out*10+jp-'0',jp=getchar(); return out*fh;}const int MAXN=329;int n,m,K;int t[MAXN][MAXN];int f[MAXN][MAXN][MAXN];void solve(){ int ans=inf; memset(f,0x7f,sizeof f); for(rg int j=1;j<m;++j) for(rg int k=j+1;k<=m;++k) f[1][j][k]=t[1][j]+t[1][k]; for(rg int i=2;i<=n;++i) { for(rg int j=1;j<m;++j) for(rg int k=j+1;k<=m;++k) f[i][j][k]=min(K+min(f[i][j-1][k],f[i][j][k-1]),f[i-1][j][k]); for(rg int j=m-1;j>=1;--j) for(int k=m;k>j;--k) f[i][j][k]=min(f[i][j][k],K+min(f[i][j+1][k],f[i][j][k+1])); for(rg int j=1;j<=m;++j) for(rg int k=j+1;k<=m;++k) f[i][j][k]+=t[i][j]+t[i][k]; } for(rg int j=1;j<m;++j) for(rg int k=j+1;k<=m;++k) ans=min(ans,f[n][j][k]); cout<<ans<<endl;}int main(){ freopen("room.in","r",stdin); freopen("room.out","w",stdout); n=read(),m=read(),K=read(); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) t[i][j]=read(); solve(); return 0;} 可以滚掉第一维?空间没卡的话就随便吧…