UVa : 10364 - Square
問題概要
M本(4以上20以下)の長さが様々な棒が与えられる。
これらM本の棒を使って正方形の4つの辺を作ることができるか答えよ。
棒は折ったり曲げたりしてはいけません。
解法
まず、正方形を絶対作れない場合を考えましょう。
棒の長さを全部足し合わせて、もしこの数字が4で割り切れなければ絶対に正方形を作れないですよね。もし、4で割り切れるならば、正方形になる可能性を持っています。
ここで、(棒の長さの合計 / 4)をlenと置きます。
さて、本当に正方形を作れるかどうかは、以下のDPで解けます。
dp[正方形の辺を何本作ったか(4)][どの棒を使用したか(2^M)] = このようなパターンを作れるか否か(0 or 1)
dp[4][2^M]が1になっていれば答えはyesです。
dp[i][j]からの遷移は以下のようになります。
- jに含まれない新たな棒kを追加しようとするときのことを考える
- kを追加することで、使用している棒の長さの合計が、lenの倍数になるならば
- dp[i + 1][j | (1 << k)]が1になる
- これは、lenの倍数になると、正方形の辺が1本増えるため
- kを追加することで、使用している棒の長さの合計が、len*(i+1)より小さくなるならば
- dp[i][j | (1 << k)]が1になる
- もし、長さの合計がlen*(i+1)より大きくなってしまう場合は、棒を折り曲げたりしないと、正方形の辺を増やすことができないので無視
- kを追加することで、使用している棒の長さの合計が、lenの倍数になるならば
プログラム
#include <iostream> #include <cstring> using namespace std; int n; int t[21]; bool dp[5][1 << 21]; void solve(){ int sum = 0; for(int i = 0; i < n; i++){ sum += t[i]; } if(sum % 4 != 0){ cout << "no" << endl; return; } int len = sum / 4; int maxBit = 1 << n; memset(dp, 0, sizeof(dp)); dp[0][0] = true; for(int i = 0; i < 4; i++){ for(int j = 0; j < maxBit; j++){ if(!dp[i][j]) continue; //使用している棒の長さの合計を求める sum = 0; for(int k = 0; k < n; k++){ if(!(j & (1 << k))) continue; sum += t[k]; } //現在構築中の辺の長さだけ取得 sum -= i * len; for(int k = 0; k < n; k++){ if(j & (1 << k)) continue; if(sum + t[k] == len){ //今構築中の辺が完成する場合 dp[i + 1][j | (1 << k)] = true; } else if(sum + t[k] < len){ //今構築中の辺がまだ完成しない場合 dp[i][j | (1 << k)] = true; } } } } cout << (dp[4][maxBit - 1] ? "yes" : "no") << endl; } int main(){ int T; cin >> T; while(T--){ cin >> n; for(int i = 0; i < n; i++){ cin >> t[i]; } solve(); } }