Google Codejam 2009 Round 2

https://code.google.com/codejam/contest/204113/dashboard#s=p1


小的case是暴搜过的。在一个位置有四种选择,左走,右走,左挖,右挖。

记得刚右走一步之后,不能马上左走,要不死循环了。

一个剪枝是如果当然挖的多余当前最好的解。

#include <iostream>
#include <string.h>
#include <math.h>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <stdio.h>
#include <string>
#include <cctype>
#include <ctime>
#include <strstream>
typedef long long ll;
using namespace std;
#define N 55
int R,C,F,ans;
bool ca[N][N];
struct sol
{
	int x,y,di,la;//la=-1×ó±ß£¬0ÎÞ£¬1ÓÒ±ß
	sol(){};
	sol(int _x,int _y,int _d,int _l)
	{
		x=_x;
		y=_y;
		di=_d;
		la=_l;
	}
};
void se(sol c)
{
	int i;
	if(c.x==R-1)
	{
		if(c.di<ans)
		{
			ans=c.di;
		}
		return;
	}
	if(c.di>=ans)return;
	if(c.y<C-1)
	{
		if(c.la!=1&&ca[c.x][c.y+1]==0&&ca[c.x+1][c.y+1])se(sol(c.x,c.y+1,c.di,-1));
		if(ca[c.x][c.y+1]==0&&ca[c.x+1][c.y+1]==0)
		{
			i=c.x+2;
			while(ca[i][c.y+1]==0)i++;
			if(i-c.x-1<=F)se(sol(i-1,c.y+1,c.di,0));
		}
		if(ca[c.x][c.y+1]==0&&ca[c.x+1][c.y+1])
		{
			ca[c.x+1][c.y+1]=0;
			se(sol(c.x,c.y,c.di+1,0));
			ca[c.x+1][c.y+1]=1;
		}
	}
	if(c.y>0)
	{
		if(c.la!=-1&&ca[c.x][c.y-1]==0&&ca[c.x+1][c.y-1])se(sol(c.x,c.y-1,c.di,1));
		if(ca[c.x][c.y-1]==0&&ca[c.x+1][c.y-1]==0)
		{
			i=c.x+2;
			while(ca[i][c.y-1]==0)i++;
			if(i-c.x-1<=F)se(sol(i-1,c.y-1,c.di,0));
		}
		if(ca[c.x][c.y-1]==0&&ca[c.x+1][c.y-1])
		{
			ca[c.x+1][c.y-1]=0;
			se(sol(c.x,c.y,c.di+1,0));
			ca[c.x+1][c.y-1]=1;
		}
	}
}
int main()
{
	ios_base::sync_with_stdio(false);
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	int cas;
	int ki,i,j;
	char ch;
	scanf("%d",&cas);
	for(ki=1;ki<=cas;ki++)
	{
		printf("Case #%d: ",ki);
		//cin>>R>>C>>F;
		scanf("%d%d%d",&R,&C,&F);
		for(i=0;i<R;i++)
		{
			ch=getchar();
			for(j=0;j<C;j++)
			{
				ch=getchar();
				if(ch=='.')ca[i][j]=0;
				else ca[i][j]=1;
			}
		}
		for(j=0;j<C;j++)
			ca[R][j]=1;
		ans=99999;
		se(sol(0,0,0,0));
		if(ans==99999)puts("No");
		else printf("Yes %d\n",ans);
		fflush(stdout);
	}
	return 0;
}

大的case是看了解析之后,才做的。看完也想了好久。

我开的是50*50*50*2*2的数组。dp[x][l][r][bl][br]来存储

刚到第x行时,最左可以走到l位置,最右可以走到r位置,bl,br=0表示l或者r处可以落下去,bl,br=1表示l或者r外是墙,l,r处不能落。

发现搜索的顺序,会影响结果:要先搜拆的少的情况,后搜拆的多的。

下面是代码。

#include <iostream>
#include <string.h>
#include <math.h>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <stdio.h>
#include <string>
#include <cctype>
#include <ctime>
#include <strstream>
typedef long long ll;
using namespace std;
#define N 55
#define INF 99999
int R,C,F,ans;
bool ca[N][N];
int dp[N][N][N][2][2];
void dis()
{
	int i,j;
	for(i=0;i<R;i++)
	{
		for(j=0;j<C;j++)
			printf("%d ",ca[i][j]);
		puts("");
	}
}
int se(int x,int y,int l,int r,bool bl,bool br)//bl==1×ó±ßÊÇǽ£¬0×ó±ßÊÇ¿Õ
{
	if(x==R-1)return 0;
	if(l==-2)
	{
		bl=br=1;
		for(l=y;l>=0;l--)
		{
			if(ca[x][l])
			{
				break;
			}
			if(!ca[x+1][l])
			{
				bl=0;
				break;
			}
		}
		if(bl)l++;
		for(r=y;r<C;r++)
		{
			if(ca[x][r])
			{
				break;
			}
			if(!ca[x+1][r])
			{
				br=0;
				break;
			}
		}
		if(br)r--;
	}
	if(dp[x][l][r][bl][br]!=-1)return dp[x][l][r][bl][br];
	//printf("%d %d %d %d %d %d\n",x,y,l,r,bl,br);
	if(bl&&br&&l==r)return dp[x][l][r][bl][br]=INF;
	int i,j,k;
	set<int> ret;
	ret.insert(INF);
	if(!bl)
	{
		i=x+2;
		while(ca[i][l]==0)i++;
		if(i-x-1<=F)ret.insert(se(i-1,l,-2,0,0,0));
		ret.insert(se(x,y,l+1,r,1,br));
		for(j=1;l+j<r;j++)
			if(ca[x+1][l+j+1])
			{
				ca[x+1][l+j]=0;
				i=x+2;
				while(ca[i][l+j]==0)i++;
				if(i-x-1<=F)ret.insert(se(i-1,l+j,-2,0,0,0)+j);
			}
		for(j=1;l+j<r;j++)
			ca[x+1][l+j]=1;
	}
	else if(ca[x+1][l+1])
	{
		ret.insert(se(x,y,l+1,r,1,br));
		ca[x+1][l]=0;
		ret.insert(se(x,y,l,r,0,br)+1);
		ca[x+1][l]=1;
	}
	if(!br)
	{
		i=x+2;
		while(ca[i][r]==0)i++;
		if(i-x-1<=F)ret.insert(se(i-1,r,-2,0,0,0));
		ret.insert(se(x,y,l,r-1,bl,1));
		for(j=1;r-j>l;j++)
			if(ca[x+1][r-j-1])
			{
				ca[x+1][r-j]=0;
				i=x+2;
				while(ca[i][r-j]==0)i++;
				if(i-x-1<=F)ret.insert(se(i-1,r-j,-2,0,0,0)+j);
			}
		for(j=1;r-j>l;j++)
			ca[x+1][r-j]=1;
	}
	else if(ca[x+1][r-1])
	{
		ret.insert(se(x,y,l,r-1,bl,br));
		ca[x+1][r]=0;
		ret.insert(se(x,y,l,r,bl,0)+1);
		ca[x+1][r]=1;
	}
	//dis();
	//printf("%d %d %d %d %d\t\t%d\n\n",x,l,r,bl,br,*ret.begin());
	return dp[x][l][r][bl][br]=*ret.begin();
}
int main()
{
	ios_base::sync_with_stdio(false);
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	int cas;
	int ki,i,j;
	char ch;
	scanf("%d",&cas);
	for(ki=1;ki<=cas;ki++)
	{
		printf("Case #%d: ",ki);
		//cin>>R>>C>>F;
		scanf("%d%d%d",&R,&C,&F);
		memset(dp,-1,sizeof(dp));
		for(i=0;i<R;i++)
		{
			ch=getchar();
			for(j=0;j<C;j++)
			{
				ch=getchar();
				if(ch=='.')ca[i][j]=0;
				else ca[i][j]=1;
			}
		}
		for(j=0;j<C;j++)
			ca[R][j]=1;
		ans=se(0,0,-2,0,0,0);
		if(ans>=INF)puts("No");
		else printf("Yes %d\n",ans);
		fflush(stdout);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值