[CLYZ2017]day4

猜测

image

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;
}

开房间

image

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;
}
posted @ 2021-11-26 20:29  Aireen_Ye  阅读(27)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.