[CLYZ2017]day4
猜测
soulution
100分
\(x,y\)坐标可以建二分图.
因为求的是所有合法猜测猜对的个数的最小值,所以考虑最小费用最大流.
从\(s\)向\(x_i\)连一条容量为\(x_i\)出现次数,费用为\(0\)的有向边;
从\(y_i\)向\(t\)连一条容量为\(y_i\)出现次数,费用为\(0\)的有向边;
若\((x_i,y_i)\)为特殊格子,从\(x_i\)向\(y_i\)连一条容量为\(1\),费用为\(1\)的有向边;否则,从\(x_i\)向\(y_i\)连一条容量为\(1\),费用为\(0\)的有向边.
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 105
#define M 10005
#define K 100005
using namespace std;
struct graph{
int nxt,to,f,w;
}e[M];
struct point{
int e,v;
}pre[N];
bool inq[N];queue<int> q;
int x[N],y[N],px[K],py[K],tx[K],ty[K];
int b[N][N],g[N],dis[N],n,m,s,t,cnt=1,tot;
inline void addedge(int x,int y,int f,int w){
e[++cnt].nxt=g[x];g[x]=cnt;
e[cnt].to=y;e[cnt].f=f;e[cnt].w=w;
}
inline void adde(int x,int y,int f,int w){
addedge(x,y,f,w);addedge(y,x,0,-w);
}
inline bool spfa(int u){
for(int i=1;i<=t;++i)
dis[i]=M;
q.push(u);dis[u]=0;inq[u]=true;
while(!q.empty()){
u=q.front();q.pop();inq[u]=false;
for(int i=g[u];i;i=e[i].nxt)
if(e[i].f>0&&dis[u]+e[i].w<dis[e[i].to]){
dis[e[i].to]=dis[u]+e[i].w;
pre[e[i].to].e=i;pre[e[i].to].v=u;
if(!inq[e[i].to]){
q.push(e[i].to);inq[e[i].to]=true;
}
}
}
return dis[t]<M;
}
inline int mf(){
int ret=0,d;
while(spfa(s)){
d=n;
for(int i=t;i!=s;i=pre[i].v)
d=min(d,e[pre[i].e].f);
ret+=d*dis[t];
for(int i=t;i!=s;i=pre[i].v){
e[pre[i].e].f-=d;
e[pre[i].e^1].f+=d;
}
}
return ret;
}
inline void Aireen(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d%d",&x[i],&y[i]);
++tx[x[i]];++ty[y[i]];
}
for(int i=1;i<K;++i)
if(tx[i]) px[i]=++tot;
m=tot;
for(int i=1;i<K;++i)
if(ty[i]) py[i]=++tot;
s=tot+1;t=s+1;
for(int i=1;i<K;++i)
if(tx[i]) adde(s,px[i],tx[i],0);
for(int i=1;i<K;++i)
if(ty[i]) adde(py[i],t,ty[i],0);
for(int i=1;i<=n;++i)
b[px[x[i]]][py[y[i]]]=1;
for(int i=1;i<=m;++i)
for(int j=m+1;j<=tot;++j)
adde(i,j,1,b[i][j]);
printf("%d\n",mf());
}
int main(){
freopen("guess.in","r",stdin);
freopen("guess.out","w",stdout);
Aireen();
fclose(stdin);
fclose(stdout);
return 0;
}
开房间
60分
\(f[i][j][k](j<k)\)表示第\(i\)关,\(A\)在\(j\),\(B\)在\(k\)的最小消耗.
\(f[i][j][k]=min\{f[i-1][l][q]+|j-l|+|k-q|\}+t_{i,l}+t_{i,q}(j<k,l<q)\)
\(ans=min\{f[n][j][k]\}(j<k)\).
100分
类似完全背包那样优化.
\(f[i][j][k]=min\{f[i-1][j][k],f[i][j-1][k],f[i][j+1][k],f[i][j][k-1],f[i][j][k+1]\}\).
通过注意枚举顺序,还可以把\(i\)那一维压掉.
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 305
#define INF 600000000
using namespace std;
int f[N][N],t[N][N],n,m,k,ans;
inline int min(int x,int y){
return x<y?x:y;
}
inline void Aireen(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
scanf("%d",&t[i][j]);
for(int i=0;i<=m+1;++i)
for(int j=0;j<=m+1;++j)
f[i][j]=INF;
for(int j=1;j<=m;++j)
for(int l=j+1;l<=m;++l)
f[j][l]=t[1][j]+t[1][l];
for(int i=2;i<=n;++i){
for(int j=1;j<=m;++j)
for(int l=j+1;l<=m;++l)
f[j][l]=min(f[j][l],min(f[j-1][l],f[j][l-1])+k);
for(int j=m;j;--j)
for(int l=m;l>j;--l)
f[j][l]=min(f[j][l],min(f[j+1][l],f[j][l+1])+k);
for(int j=1;j<=m;++j)
for(int l=j+1;l<=m;++l)
f[j][l]+=t[i][j]+t[i][l];
}
ans=INF;
for(int i=1;i<=m;++i)
for(int j=i+1;j<=m;++j)
ans=min(ans,f[i][j]);
printf("%d\n",ans);
}
int main(){
freopen("room.in","r",stdin);
freopen("room.out","w",stdout);
Aireen();
fclose(stdin);
fclose(stdout);
return 0;
}