SRM 444 Div1

試験的に図書館から参加.
22:00まで空いていると思いこんでいたけど,実際は21:30までらしい.

結果

Easy(250) 116.50pts
Medium(500) 371.19
Hard(1000) Opened

Easy

最初はO(N^4)で頑張ろうと思ったが実装ミスが怖くなったので,安全なO(N^5)のコードを書くことに.
結構時間を掛けてしまったが無事Accepted

実装(Visual Basic.NET)
Imports Microsoft.VisualBasic
Imports System
Imports System.Collections
Imports System.Text
Imports System.Collections.Generic
Imports System.IO
Imports System.Math
Imports Weight = System.Int32

Public Class UnfoldingTriangles
    Dim W As Integer, H As Integer
    Dim MAP(50, 50) As Integer
    Public Function Check(ByVal X As Integer, ByVal Y As Integer, ByVal Size As Integer, ByVal Limit As Integer) As Boolean
        Dim yy As Integer = Y
        Dim xt As Integer = X
		Dim I as Integer
        For xx As Integer = X - Size + 1 To X
            If xt < 0 OrElse Y >= H Then Return False
            If (MAP(xt, yy) <> 2) Then Return False
            yy += 1
            xt -= 1
        Next
        '次に.を含まないか判定
        'MsgBox("!")
        yy = Y
        Dim Cnt As Integer = 0
        xt = X
        For xx As Integer = X - Size + 1 To X
            For i = yy + 1 To Y + Size - 1
                If (MAP(xt, i) = 0) Then Return False
                If MAP(xt, i) = 2 Then Cnt += 1
            Next
            yy += 1
            xt -= 1
        Next
        If Cnt > Limit Then Return False

        '周囲に1が無いかどうか
        For i = X - Size + 1 To X
            If MAP(i, Y + Size) = 1 Then Return False
        Next
        For i = Y To Y + Size - 1
            If MAP(X + 1, i) = 1 Then Return False
        Next

        Check = True
    End Function
    Public Function Calc(ByVal X As Integer, ByVal Y As Integer, ByVal Limit As Integer) As Integer
        Dim Ans As Integer = 0
        If MAP(X, Y) <> 2 Then Return -1
        For Calc = 1 To 51
            If (Check(X, Y, Calc, Limit)) Then
                Ans = Max(Ans, Calc)
            End If
        Next
        If Ans = 0 Then
            Return -1
        Else
            Return Ans * (Ans + 1) / 2
        End If
    End Function
    Public Sub DPrint()
        For i As Integer= 0 To H - 1
            For j As Integer= 0 To W - 1
                Select MAP(j, i)
                    Case 1
                        'Debug.Write("#")
                    Case 2
                        'Debug.Write("/")
                    Case 0
                        'Debug.Write(".")
                End Select
            Next
            'Debug.WriteLine("")
        Next i
        'Debug.WriteLine("")
    End Sub
    Public Function solve(ByVal grid As String(), ByVal unfoldLimit As Integer) As Integer
        Dim i As Integer, j As Integer
        solve = -1
        H = grid.Length
        W = grid(0).Length

        '0白 1黒 2斜め
        For i = 0 To H - 1
            For j = 0 To W - 1
                Dim T As Integer = 0
                Select Case Mid(grid(i), j + 1, 1)
                    Case "#"
                        T = 1
                    Case "/"
                        T = 2
                End Select

                MAP(j, i) = T
            Next
        Next
        DPrint()

        For i = 0 To H - 1
            For j = 0 To W - 1
                solve = Max(solve, Calc(j, i, unfoldLimit))
            Next
        Next
    End Function
End Class

Medium

見た瞬間,4の個数を最大化するBitDPと気づいたが,
最初は前2列の状態を保存しておく必要があると思いこんでしまい,時間制限的な問題でC++で解くことにした.
最終的に書き終わってから保存する必要があるのは前1列+2個ということに気づき修正し提出.

実装(C++)
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstring>
using namespace std;
#define REP(i,x) for(int i=0;i<(int)x;i++)
class FourBlocks {
public:
	int maxScore(vector <string>);
};
int W,H;
int MAP[10][25];
char dp[11][26][1<<14];
int solve(int x,int y,int forward){
	if(dp[x][y][forward]>=0)return dp[x][y][forward];
	//cout<<x<<","<<y<<";"<<forward<<endl;
	//置く場合
	int nx,ny;
	int res=0;
	nx=x+1,ny=y;
	if(nx>=W)nx=0,ny++;
	if(ny==H)return 0;
	if(W-x>=2&&H-y>=2){
		//置けるかどうか
		if((forward&1)==0)
			if(((forward>>1)&1)==0){
				if(((forward>>W)&1)==0){
					if(((forward>>W+1)&1)==0){
						if(MAP[x][y]==0&&MAP[x+1][y]==0&&MAP[x][y+1]==0&&MAP[x+1][y+1]==0){
							res=solve(nx,ny,(forward|1|2|(1<<W)|(1<<W+1))>>1)+1;
						}
					}
				}
			}
	}
	res=max(res,solve(nx,ny,forward>>1));
	return dp[x][y][forward]=res;
}
int FourBlocks::maxScore(vector <string> grid) {
	W=grid.size();
	H=grid[0].size();
	memset(dp,-1,sizeof(dp));
	REP(i,W)REP(j,H){
		MAP[i][j]=grid[i][j]=='1';
	}
	return W*H+12*solve(0,0,0);
}

メモ

Mediumのid:iakasTさんの解法を見た所,メモ化再帰の仕方がスマートだったのでメモ.

int dp[][];//-1で初期化
int solve(int x,int y){
  int &res=dp[x][y];
  if(res==-1){
     //ここに処理を入れる.
  }
  return res;
}

この方法だとメモ化のON/OFFも簡単に出来そう.