ラベル 移動 の投稿を表示しています。 すべての投稿を表示
ラベル 移動 の投稿を表示しています。 すべての投稿を表示

SRM 368 DIV1 Easy - JumpingBoard

SRM 368 DIV1 Easy - JumpingBoard

問題


http://community.topcoder.com/stat?c=problem_statement&pm=8245&rd=10936

あるボードが存在し、その中には1~9までの数字かHで表わされる穴が存在する。
まずは一番左上のセルからスタートし、そのセルに書いてある数だけ上下左右に移動する。ボードの外に出たらそこで終了する。

このとき、ボードの外に出るまでの最大のターン数を求める。
ただし、ループが可能でボードの外に出なくてもよい場合はー1を返す。

解き方


BFSに最初見えたがDFSにて全てのルートを探索する必要がある。
ただし、一度通ったルートはメモ化が可能なのでdpが適用できる。

注意する点はループが発生した場合はー1を返さなければならないので、
現在探索中のルートはフラグを立てて判定しなければならない。

今回は以下のように値を割り振ることでコーディング可能。

セルを超えた時   :0で終了
すでに探索したルート:0以上で終了(うち、ホールにあたったときは0で終了)
現在探索中のルート :ー2で例外値を返し終了
それ以外      :-1で次のループに進む

他の方のコードをみましたが、throw&catchしてもよさそうでした。
http://community.topcoder.com/stat?c=problem_solution&rd=10936&pm=8245&cr=251074

コード


using namespace std;

#define all(c) (c).begin(),(c).end()
#define FORE(i,d,e) for(int i=d;i<e;i++)
#define FOR(i,s,e) for (int i = int(s); i != int(e); i++)
#define FORIT(i,c) for (typeof((c).begin()) i = (c).begin(); i != (c).end(); i++)
#define ISEQ(c) (c).begin(), (c).end()

int dp[60][60];
int w,h;
int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
vector<string> B;

class JumpingBoard {

public:

int f(int r,int c){
if(r<0||r>=h||c<0||c>=w)return 0;
if(dp[r][c]>=0)return dp[r][c];
if(dp[r][c]==-2)return 900000;

int ret=0;
int move=B[r][c]-'0';

dp[r][c]=-2;
FORE(i,0,4)ret=max(ret,1+f(r+dx[i]*move,c+dy[i]*move));
return dp[r][c]=ret;
}

int maxJumps(vector<string> board) {
w=board[0].size(),h=board.size();
FORE(i,0,h)FORE(j,0,w){
if(board[i][j]=='H')dp[i][j]=0;
else dp[i][j]=-1;
}
B=board;
int ret=f(0,0);

return ret>900000 ? -1 : ret;
}

};

using namespace std;

#define all(c) (c).begin(),(c).end()
#define FORE(i,d,e) for(int i=d;i<e;i++)
#define FOR(i,s,e) for (int i = int(s); i != int(e); i++)
#define FORIT(i,c) for (typeof((c).begin()) i = (c).begin(); i != (c).end(); i++)
#define ISEQ(c) (c).begin(), (c).end()

int visited[60][60],next[60][60];

class JumpingBoard {

public:

int maxJumps(vector<string> board) {
int h=board.size(),w=board[0].size();

int dr[]={0,0,1,-1};
int dc[]={1,-1,0,0};
int INF=h*w+10,ret=0;

memset(next,0,sizeof(next));
memset(visited,0,sizeof(visited));
visited[0][0]=1;

while(ret<INF){
int valid=0;
memset(next,0,sizeof(next));
ret++;

FORE(i,0,h)FORE(j,0,w)if(visited[i][j]){
int cnt=board[i][j]-'0';
FORE(k,0,4){
int nr=i+cnt*dr[k];
int nc=j+cnt*dc[k];
if(0<=nr && nr<h && 0<=nc && nc<w && board[nr][nc]!='H'){
next[nr][nc]=1;
valid=1;
}
}
}
FORE(i,0,h)FORE(j,0,w)visited[i][j]=next[i][j];
if(!valid)break;
}

return ret==INF ? -1 :ret;
}

};

SRM 303 DIV1 Easy - SpiralNumbers

SRM 303 DIV1 Easy - SpiralNumbers

問題


http://community.topcoder.com/stat?c=problem_statement&pm=6093&rd=9824

座標上に1から始まり、らせん上に数が増えていく。

この座標の1の場所を(0,0)として右側を正のx座標、下側を正のy座標としたときに与えられた数字の座標の位置を求める。

解き方


数字の最大が1e+9なので全探索では解くことができない。
ここで、各四角形を考えると右上の座標の数は1,9,25と(i+2)^2ずつ増えていることがわかる。また、その座標は(1、-1)、(2、-2)とこちらも1ずつ増減していることがわかる。

この法則に従ってどの四角形に与えられた数字があるかを判定し、逆にらせんを描くことで答えを求めることができる。

コード


using namespace std;

#define all(c) (c).begin(),(c).end()
#define FORE(i,d,e) for(int i=d;i<e;i++)
#define FOR(i,s,e) for (int i = int(s); i != int(e); i++)
#define FORIT(i,c) for (typeof((c).begin()) i = (c).begin(); i != (c).end(); i++)
#define ISEQ(c) (c).begin(), (c).end()

class SpiralNumbers {

public: string getPosition(int N) {
int dx[]={-1,0,1,0};
int dy[]={0,1,0,-1};
int x=0,y=0,len=0;

if(N==1)return "(0,0)";
for(long long i=1;i*i<=1e+18;i+=2){
len++;
if(i*i<N)continue;
x=len-1,y=-(len-1);
int num=i*i;
FORE(dir,0,4){
FORE(k,0,i-1){
if(num==N)goto end;
x+=dx[dir],y+=dy[dir];
num--;
}
}

}
end:;
char ch[40];
sprintf(ch,"(%d,%d)",y,x);

return ch;
}

};

SRM 604 DIV1 Easy - PowerOfThree

SRM 604 DIV1 Easy - PowerOfThree

問題


http://community.topcoder.com/stat?c=problem_statement&pm=12917&rd=15837

ロボットが座標(0,0)からスタートし、4方向に移動する。
ステップ0からスタートし、各ステップ3^Kだけ指定した方向に移動する。

移動したい座標(x、y)が与えられた時、その座標に移動できるなら”Possible”、移動できないなら"Impossible"を返す。

解き方


足し引きできる数は3の階乗なので、
ビット計算に落とし込むことで解くことができる。

座標の正負は関係ないので正の座標に変換し、
例としてサンプルの一つを3進数で考えると以下のように変換できる。
x座標:1=001
y座標:9=100

ここで3の階乗を足していくステップは、
3進数の右から考えていくと毎回x、yのどちらかの数を引くことと同じになる。

ここでどちらかが1であればよいが、
どちらも0、もしくはどちらも1であれば”Impossible”になる。

途中でx=0、y=0になれば"Possible"で終了になる。

コード


class PowerOfThree {

public:


string ableToGet(int x, int y) {
x=abs(x);
y=abs(y);

while(!(x==0&&y==0)){
if(x%3==0&&y%3==0)return "Impossible";
if(x%3!=0&&y%3!=0)return "Impossible";
if(x%3==1)x--;
if(x%3==2)x++;
if(y%3==1)y--;
if(y%3==2)y++;
x/=3,y/=3;
}
return "Possible";
}

};

SRM 525 DIV1 Easy - DropCoins

SRM 525 DIV1 Easy - DropCoins

問題


http://community.topcoder.com/stat?c=problem_statement&pm=11665&rd=14550

四角形のセルに複数コインがある。
1回の操作で上下左右に全てのコインを移動させることができ、四角形から外れたコインは落ちる。
コインの数Kが与えられた時、コインを落としてK個にできるとき最小の操作回数を返す。できないときはー1を返す。

解き方


全探索で上下左右に動いた時のコインのマスを保存して重複しないようにすればよいと思いつくが、少し複雑。

「最後に残るコインのマスはかならず四角形になる」ことがわかれば、
四角形からすべてのサブ四角形を求め、その四角形にあるコインの数がKに一致したものが答えの候補になる。
そのときの移動回数は縦横それぞれに対し、角の位置+戻るために2つのうち最小の角の位置を足してあげればよい。

コード



class DropCoins {

public: int getMinimum(vector<string> board, int K) {
int INF=1e+8, ret=INF;
int h=board.size(),w=board[0].size();

for(int x0=0;x0<h;x0++){
for(int y0=0;y0<w;y0++){
for(int x1=x0+1;x1<=h;x1++){
for(int y1=y0;y1<=w;y1++){
int coin=0;
FORE(a,x0,x1)FORE(b,y0,y1)if(board[a][b]=='o')coin++;
if(coin!=K)continue;
int a=x0,b=h-x1,c=y0,d=w-y1;
ret=min(ret,a+b+c+d+min(a,b)+min(c,d));
}
}
}
}

return ret==INF ? -1 : ret;
}

};

SRM 526 DIV1 Easy - DucksAlignment

SRM 526 DIV1 Easy - DucksAlignment

問題


http://community.topcoder.com/stat?c=problem_statement&pm=11667&rd=14551

四角形のマスにダチョウが複数存在する。
ダチョウを縦か横に一列に並べるとき、必要な最小移動数を求める。

解き方


縦に並べるときと横に並べるときの2通りを試し小さい方を返す。
縦と横それぞれに対して、並べる列/行と詰めるときの移動数を求めてあげればよい。

コード



class DucksAlignment {

public: int minimumTime(vector<string> grid) {
int h=grid.size(),w=grid[0].size();
vector<int> tate,yoko;

FORE(i,0,h){
FORE(j,0,w){
if(grid[i][j]=='o'){
tate.push_back(i);
yoko.push_back(j);
}
}
}
sort(tate.begin(),tate.end());
sort(yoko.begin(),yoko.end());

//move to one column
int tate1=1e+8,yoko1=1e+8;
FORE(i,0,h){
int tmp=0;
FORE(k,0,tate.size())tmp+=abs(i-tate[k]);
tate1=min(tate1,tmp);
}
FORE(i,0,w-yoko.size()+1){
int tmp=0,cur=i;
FORE(j,0,yoko.size()){
tmp+=abs(yoko[j]-cur);
cur++;
}
yoko1=min(yoko1,tmp);
}
int ans1=tate1+yoko1;

//move to one row
int tate2=1e+8,yoko2=1e+8;
FORE(i,0,w){
int tmp=0;
FORE(k,0,yoko.size())tmp+=abs(i-yoko[k]);
yoko2=min(yoko2,tmp);
}
FORE(i,0,h-tate.size()+1){
int tmp=0,cur=i;
FORE(j,0,tate.size()){
tmp+=abs(tate[j]-cur);
cur++;
}
tate2=min(tate2,tmp);
}
int ans2=tate2+yoko2;

return min(ans1,ans2);
}

};