読者です 読者をやめる 読者になる 読者になる

土下座しながら探索中

主に競技プログラミング

UVa 103 : Stacking Boxes

UVa DAG 動的計画法

問題リンク:Stacking Boxes

問題概要:
n個のk次元の箱がある
以下の条件を満たす時、箱aを箱bに入れることができる
・箱aの各辺の長さが箱bの対応する辺の長さ未満であるような順列が存在する
(例: 5次元とする
箱 a : 5 1 3 7 6
箱 b : 4 2 7 8 6

分かりにくいのでソートすると、
箱 a : 1 3 5 6 7
箱 b : 2 4 6 7 8
この時、箱aの各辺は全て箱bの対応する辺未満なので箱bに箱aを入れることができる

箱 a : 1 2 3 4 5
箱 b : 1 1 2 3 4
この時は、箱aの辺をどのように入れ替えても箱bの対応する各辺未満にできないので箱bに箱aを入れることはできない
逆に箱aに箱bは入れる亊ができる


最大何個の箱を入れる亊ができるか?
その時の箱の入れ方も順番通りに出力せよ(複数ある場合はどれでも良い)

解法:
トポロジカルソートしてDP
入力をとった後に、箱の各辺の長さは全て小さい順でソートしておく
あとは二重ループである箱の中に入れる亊ができる箱全てに対してコスト1の辺をはる
そうしてできたグラフをトポロジカルソートし、あとはDPで最大の数を数える
経路も記録しておき、最後に復元する

コード:

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)
#define inf (1<<26)
#define MAX 400
using namespace std;

struct P
{
  int to,cost;
  P(int to=inf,int cost=inf):to(to),cost(cost){}
};

//トポロジカルソート

int n,k;
int dp[MAX],maxpath[MAX];

bool check(vector<int> &a,vector<int> &b)
{
  rep(i,k)
    if(a[i] >= b[i])return false;
  return true;
}

int main()
{
  while(scanf("%d %d",&n,&k) != EOF)
    {

      vector<vector<P> > G(n);
      vector<int> vec[n];
      rep(i,n)
	{
	  vec[i].resize(k);
	  rep(j,k)scanf("%d",&vec[i][j]);
	  sort(vec[i].begin(),vec[i].end());
	}

      rep(from,n)
	{
	  rep(to,n)
	    {
	      if(check(vec[from],vec[to]))
		{
		  G[from].push_back(P(to,1));
		}
	    }
	}

      vector<int> order;
      topologicalSort(G,order);

      rep(i,n)dp[i] = 1,maxpath[i] = inf;

      int maxcost = 1;
      int maxpos = order[0];


      rep(i,n)
	{
	  int cur = order[i];
	  rep(j,G[cur].size())
	    {
	      int next = G[cur][j].to;
	      if(dp[next] < dp[cur]+1)
		{
		  dp[next] = dp[cur] + 1;
		  maxpath[next] = cur;
		  if(maxcost < dp[next])
		    {
		      maxcost = dp[next];
		      maxpos = next;
		    }
		}
	    }
	}
      
      cout << maxcost << endl;
      vector<int> path;
      for(;;)
	{
	  path.push_back(maxpos);
	  maxpos = maxpath[maxpos];
	  if(maxpos == inf)break;
	}
      reverse(path.begin(),path.end());
      rep(i,path.size())
	{
	  cout << path[i]+1;
	  if(i != path.size()-1)cout << " ";
	  else                  cout << endl;
	}      

    }
  return 0;
}