Problem:
Problem C: ホテル探し

Problem Statement

会津合宿に参加予定の高槻さんは、家が貧乏であまりお金を持っていない。彼女は、合宿のためにホテルを探しているが、なるべくお金を節約できるプランにするために、悩み中である。宿泊の合計金額が安くなるホテルの泊まり方を求めて、彼女に教えてあげよう。

宿泊するホテルの候補数は、N個であり、各ホテルをホテル1、ホテル2、...、ホテルNと呼ぶこととする。彼女は、これからD泊分の予約をこれらのホテルから選ぶことにしている。候補のホテルは全て、1泊ごとに宿泊料金が変動することがあるため、入力では各ホテルごとにD泊分の宿泊費が与えられる。

D泊分の宿泊費の合計が最小になるような、ホテルの泊まり方を出力せよ。ただし、D泊全て同じホテルに泊まる必要はないことに注意してほしい。安さのためであれば、彼女は1泊ごとにホテルの移動をすることも考えている。

もし、そのような宿泊方法が複数あるならば、ホテルの移動回数が最小になるような宿泊方法を出力せよ。ホテルの移動回数とは、x泊目(1 <= x < D)の宿泊ホテルとx+1泊目の宿泊ホテルが異なる場合にこれを1回の移動と考え、D泊分足し合わせた値である。

それでも1通りに定まらない場合は、辞書順で最も小さくなるような宿泊方法を出力すること。2つの宿泊ホテル番号の数列 A = {a1, a2, ..., aD} と B = {b1, b2, ..., bD} があったとき、AがBより辞書順で小さいとは、aiがbiと異なるような最初のiについて、aiがbiより小さいときのことを言う。

Input

各データセットは、以下の形式で入力される。

N D
p11 p12 ... p1D
p21 p22 ... p2D
...
pN1 pN2 ... pND

入力は、全て整数である。Nは宿泊候補のホテルの数、Dは宿泊日数である。pijは、ホテルiにおける、j泊目の宿泊費である。

Constraints

1 <= N <= 50

1 <= D <= 50

1 <= pij <= 10000

Output

各データセットに対して、以下の形式で出力せよ。

P M
stay1
stay2
...
stayD

PはD泊分の最小の宿泊費合計、Mは最小のホテル移動回数である。続いて、問題文に指定された条件で宿泊するホテルを決定し、D泊分のホテルの番号を出力せよ。stayi (1 <= stayi <= N)は、i泊目に宿泊するホテルが、ホテルstayiであることを意味する。

Sample Input 1

2 3
3000 6000 3000
4000 5500 4500

Output for the Sample Input 1

11500 2
1
2
1

最小の宿泊費合計は、11500円であり、この合計値を満たせるのは、
1泊目にホテル1に泊まり3000円、2泊目にホテル2に泊まり5500円、3泊目にホテル1に泊まり3000円の方法だけである。ここでは、1泊目と2泊目の間、2泊目と3泊目の間、の合計2回ホテルの移動を行っている。

Sample Input 2

3 4
5500 5500 5500 5500
5480 5780 5980 5980
5500 5500 5500 5500

Output for the Sample Input 2

21980 1
2
1
1
1

このサンプルの宿泊費合計の最小は21980円である。合計をこの価格にするための方法は何通りか存在するが、移動回数を最小にするならば、A = {2, 1, 1, 1}、B = {2, 3, 3, 3}の2通りにしぼられる。この2通りの内、辞書順で最小なものはAであるため、これを出力する。

Solution in C++ (judge verdict: Wrong Answer)

#include<iostream>
#include<algorithm>
#include<vector>
#include<cassert>
#include<cstdio>

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n)   REP(i,0,n)
#define inf (1<<29)
#define MAX 100
using namespace std;
typedef long long ll;

/*
dp[day][hotel][number] := minimum
 */
int dp[MAX][MAX][MAX];
int path[MAX][MAX][MAX];
int N,D,p[MAX][MAX];


int main()
{
  scanf("%d %d",&N,&D);
  rep(i,N)rep(j,D)scanf("%d",&p[i][j]);
  rep(i,MAX)rep(j,MAX)rep(k,MAX)
    {
      dp[i][j][k] = inf;
      path[i][j][k] = inf;
    }

  rep(i,N)dp[0][i][0] = 0;

  rep(day,D)
    {
      rep(hotel,N)
	{
	  rep(number,55)
	    {
	      if(dp[day][hotel][number] == inf)continue;
	      rep(next_hotel,N)
		{
		  int add = (hotel != next_hotel);
		  if(dp[day+1][next_hotel][number+add] > dp[day][hotel][number]+p[next_hotel][day])
		    {
		      dp[day+1][next_hotel][number+add] = dp[day][hotel][number]+p[next_hotel][day];
		      path[day+1][next_hotel][number+add] = hotel;
		    }
		  else if(dp[day+1][next_hotel][number+add] == dp[day][hotel][number]+p[next_hotel][day])
		    {
		      if(path[day+1][next_hotel][number+add] > hotel)path[day+1][next_hotel][number+add] = hotel;
		    }

		}
	    }
	}
    }

  int mincost,minnumber,minN;
  mincost = minnumber = minN = inf;

  rep(i,N)rep(j,55)
    {
      if(mincost >= dp[D][i][j])
	{
	  if(mincost == dp[D][i][j])
	    {
	      if(minnumber > j)
		{
		  minnumber = j;
		  minN = i;
		}
	    }
	  else
	    {
	      mincost = dp[D][i][j];
	      minnumber = j;
	      minN = i;
	    }
	}
    }

  cout << mincost << " " << minnumber << endl;
  vector<int> ph;
  int d,h,n;
  d = D,h = minN, n = minnumber;
  for(;d >= 0 && n >= 0;)
    {
      ph.push_back(h);

      int next_h = path[d][h][n];
      int next_n = n - (next_h != h);
      h = next_h;
      n = next_n;
      d--;
      if(path[d][h][n] == inf)break;
    }
  reverse(ph.begin(),ph.end());
  rep(i,ph.size())
    cout << ph[i]+1 << endl;
    

    
  return 0;
}