結果
| 問題 | No.4 おもりと天秤 | 
| コンテスト | |
| ユーザー |  | 
| 提出日時 | 2023-03-16 13:07:20 | 
| 言語 | Go (1.23.4) | 
| 結果 | 
                                AC
                                 
                             | 
| 実行時間 | 2 ms / 5,000 ms | 
| コード長 | 1,991 bytes | 
| コンパイル時間 | 11,634 ms | 
| コンパイル使用メモリ | 231,904 KB | 
| 実行使用メモリ | 5,376 KB | 
| 最終ジャッジ日時 | 2024-09-18 09:19:10 | 
| 合計ジャッジ時間 | 12,551 ms | 
| ジャッジサーバーID (参考情報) | judge3 / judge2 | 
(要ログイン)
| ファイルパターン | 結果 | 
|---|---|
| other | AC * 23 | 
ソースコード
// 可能的子集和
package main
import (
	"bufio"
	"fmt"
	"os"
)
func main() {
	in := bufio.NewReader(os.Stdin)
	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()
	var n int
	fmt.Fscan(in, &n)
	nums := make([]int, n)
	sum := 0
	for i := 0; i < n; i++ {
		fmt.Fscan(in, &nums[i])
		sum += nums[i]
	}
	_, ok := subsetSum(nums, sum/2)
	can := ok || sum == 0
	if can && sum%2 == 0 {
		fmt.Fprintln(out, "possible")
	} else {
		fmt.Fprintln(out, "impossible")
	}
}
// 能否用nums中的若干个数凑出和为target
//  O(n*max(nums)))
func subsetSum(nums []int, target int) (res []int, ok bool) {
	if target <= 0 {
		return
	}
	n := len(nums)
	max_ := 0
	for _, v := range nums {
		max_ = max(max_, v)
	}
	right, curSum := 0, 0
	for right < n && curSum+nums[right] <= target {
		curSum += nums[right]
		right++
	}
	if right == n && curSum != target {
		return
	}
	offset := target - max_ + 1
	dp := make([]int, 2*max_)
	for i := range dp {
		dp[i] = -1
	}
	pre := make([][]int, n)
	for i := range pre {
		pre[i] = make([]int, 2*max_)
		for j := range pre[i] {
			pre[i][j] = -1
		}
	}
	dp[curSum-offset] = right
	for i := right; i < n; i++ {
		ndp := make([]int, len(dp))
		copy(ndp, dp)
		p := pre[i]
		a := nums[i]
		for j := 0; j < max_; j++ {
			if ndp[j+a] < dp[j] {
				ndp[j+a] = dp[j]
				p[j+a] = -2
			}
		}
		for j := 2*max_ - 1; j >= max_; j-- {
			for k := ndp[j] - 1; k >= max(dp[j], 0); k-- {
				if ndp[j-nums[k]] < k {
					ndp[j-nums[k]] = k
					p[j-nums[k]] = k
				}
			}
		}
		dp = ndp
	}
	if dp[max_-1] == -1 {
		return
	}
	used := make([]bool, n)
	i, j := n-1, max_-1
	for i >= right {
		p := pre[i][j]
		if p == -2 {
			used[i] = !used[i]
			j -= nums[i]
			i--
		} else if p == -1 {
			i--
		} else {
			used[p] = !used[p]
			j += nums[p]
		}
	}
	for i >= 0 {
		used[i] = !used[i]
		i--
	}
	for i := 0; i < n; i++ {
		if used[i] {
			res = append(res, i)
		}
	}
	ok = true
	return
}
func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}
            
            
            
        