AOJ1117 Missing Numbers
問題リンク Missing Numbers
- 概要
(p+1)行(s+1)列からなる売上表がある。各行のs+1列目はその行の売上合計、各列のp+1行目はその列の売上合計を表す。
売上表の一部が欠損しており復元したい。復元の仕方がただ1通りに求まるならば復元し、復元方法が複数あったり不可能な場合は"NO"と答えよ。なお、売上には赤字もあるため、負の値があっても構わない。また、売上は整数でなければならない。
以下のことが保証されている
合計を表す列、行に欠損はない
入力には必ず1か所以上の欠損がある
p < 100
s < 10
-1000 <= 売上 <= 1000
-100000 <= 売上合計 <= 100000
- 解法
欠損箇所に入れる数字の候補が無限にあるため、アプローチしづらい問題だと感じましたが、貪欲法で解くことができました。
基本に立ち直って、ある列か行に、欠損箇所が1つしかない場所があったら、その箇所は決定することができます。これを繰り返します。繰り返しの結果、表が全部埋まったら解となります。
では、表が埋まり切らなかった場合、つまり欠損箇所がある行と列全てが2か所以上の欠損箇所を持っている場合、実はこれはサンプル2の例と合致している状況で、復元方法が一意に定まらなくなっています。連立方程式を作ってみても解が一意に出てきません。
結局、確定するセルを埋めていって、埋めきったら解、駄目だったら"NO"となります。
- ソース
import java.util.PriorityQueue; import java.util.Scanner; //Missing Numbers public class AOJ1117 { class R implements Comparable<R>{ boolean row; int p, c; public R(boolean row, int p, int c) { this.row = row; this.p = p; this.c = c; } public int compareTo(R o) { return c-o.c; } } void run(){ Scanner sc = new Scanner(System.in); int NOT = 1<<29; for(boolean head=true;;head=false){ int h = sc.nextInt(); if(h==0)break; int w = sc.nextInt(); if(!head)System.out.println(); int[][] a = new int[h+1][w+1]; boolean[][] m = new boolean[h+1][w+1]; for(int i=0;i<=h;i++)for(int j=0;j<=w;j++){ String s = sc.next(); m[i][j] = "?".equals(s); if(m[i][j])a[i][j]=NOT; else a[i][j]=Integer.parseInt(s); } boolean ok = false; for(;;){ PriorityQueue<R> q = new PriorityQueue<R>(); int[] rc = new int[h], cc = new int[w]; for(int i=0;i<h;i++)for(int j=0;j<w;j++){ if(a[i][j]==NOT){ rc[i]++; cc[j]++; } } for(int i=0;i<h;i++)if(0<rc[i])q.add(new R(true, i, rc[i])); for(int j=0;j<w;j++)if(0<cc[j])q.add(new R(false, j, cc[j])); if(q.isEmpty()){ ok = true; break; } R r = q.poll(); if(2<=r.c)break; if(r.row){ int pj = -1, v = a[r.p][w]; for(int j=0;j<w;j++){ if(a[r.p][j]==NOT)pj = j; else v-=a[r.p][j]; } a[r.p][pj] = v; } else{ int pi = -1, v = a[h][r.p]; for(int i=0;i<h;i++){ if(a[i][r.p]==NOT)pi = i; else v-=a[i][r.p]; } a[pi][r.p] = v; } } if(ok){ for(int i=0;i<h;i++)for(int j=0;j<w;j++)if(m[i][j])System.out.println(a[i][j]); } else System.out.println("NO"); } } public static void main(String[] args) { new AOJ1117().run(); } }