zoukankan      html  css  js  c++  java
  • CF1225G To Make 1

    一、题目

    点此看题

    注意翻译中有两点没说到:(a_i) 不被 (k) 整除;(sum a_ileq 2000)

    二、解法

    不难写出 (O(3^nsum a_i)) 的子集枚举,但是显然 ( t T) 飞并且无法优化。

    深究这样 (dp) 不行的原因:其实是插入的 (x+y) 需要立即除以 (k),这导致我们难以判断某一状态的优劣,难以去除无效转移;并且这个转移具有强烈的顺序性,只能通过暴力 (dp) 考虑所有的组合方式。

    被逼入绝境的我们只能找结论了,注意一定要找必要条件再证明其充分性(充分条件真的找不出来),思考本题顺序性是最大的难点,那么我们找的结论就一定要帮助我们逃离顺序性的桎梏。

    考虑合法解中每个数,每个数被 (k) 除的次数是不同的,那么我们就考虑这个被除的次数 (b_i),所有数被除以后加起来是 (1),那么必要条件显然是存在一个非负 (b) 序列使得:(sum a_icdot k^{-b_i}=1)

    然后考虑证明其充分性,也就是如果有 (b) 序列满足 (sum a_icdot k^{-b_i}=1) 能推出一组合法解,证明可以考虑归纳法,首先序列长为 (1/2) 时是对的,当序列长为 (n>2) 时,设 (alpha=max b_i),如果两个 (alpha) 合并之后能被 (k) 整除,就能让 (b_i) 变小归纳下去,否则再和 (alpha) 合并, 因为分母是 (k^{alpha}) 而且最后求和是 (1),所以不可能出现归纳不下去的情况,充分性得也。


    根据这个结论设计 (dp),不难发现现在我们要决策 (b) 序列,如果你看懂了证明就会发现大的 (b_i) 内部合并完之后要到更小的 (b_i) 才行,所以我们(b_i) 从大到小的顺序来 (dp),但在实现中就把以前集合 (s) 中的 (b_i) 全部加 (1) 即可。

    (dp[s][p]) 表示集合 (s) 的求和是 (p) 可不可行,转移:

    • 向集合中加入 (b_i=0) 的一个 (a_i)(dp[s|2^i][p+a_i]leftarrow dp[s][p])
    • 把集合中的 (b_i) 全部增加 (1)(dp[s][p]leftarrow dp[s][pcdot k]),这个要用完全背包的转移顺序。

    不难发现可以用 ( t bitset) 优化,时间复杂度 (O(frac{1}{w}ncdot 2^ncdot sum a_i))

    三、总结

    找结论的小 ( t tirck):考虑需要解决什么问题,找对应的结论;找必要性结论(充分性一般归纳证明)

    决策具有某种性质的序列问题,用状压来定大小顺序,然后考虑整体加值。

    #include <cstdio>
    #include <bitset>
    #include <iostream>
    #include <queue>
    using namespace std;
    #define pii pair<int,int>
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,k,a[20],b[20];bitset<2005> dp[1<<16];
    void dfs(int s,int p)
    {
    	if(!s) return ;
    	if(p*k<=m && dp[s][p*k])
    	{
    		for(int i=0;i<n;i++)
    			if(s&(1<<i)) b[i+1]++; 
    		dfs(s,p*k);
    		return ;
    	}
    	for(int i=0;i<n;i++)
    		if((s&(1<<i)) && a[i+1]<=p && dp[s^(1<<i)][p-a[i+1]])
    		{
    			dfs(s^(1<<i),p-a[i+1]);
    			return ;
    		}
    }
    signed main()
    {
    	n=read();k=read();
    	dp[0][0]=1;
    	for(int i=1;i<=n;i++)
    		a[i]=read(),m+=a[i];
    	for(int s=1;s<(1<<n);s++)
    	{
    		for(int i=1;i<=n;i++)
    			if(s&(1<<i-1))
    				dp[s]|=dp[s^(1<<i-1)]<<a[i];
    		for(int i=m/k;i>=1;i--)//attention the order
    			dp[s][i]=dp[s][i]|dp[s][i*k];
    	}
    	if(!dp[(1<<n)-1][1])
    	{
    		puts("NO");
    		return 0;
    	}
    	puts("YES");
    	dfs((1<<n)-1,1);
    	priority_queue<pii> q;
    	for(int i=1;i<=n;i++)
    		q.push(make_pair(b[i],a[i]));
    	while(q.size()>=2)
    	{
    		pii t1=q.top();q.pop();
    		pii t2=q.top();q.pop();
    		printf("%d %d
    ",t1.second,t2.second);
    		int x=t1.second+t2.second,y=t1.first;
    		while(x%k==0 && y) y--,x/=k;
    		q.push(make_pair(y,x));
    	}
    }
    
  • 相关阅读:
    Salesforce 大量数据部署的最佳实践
    Salesforce 数据备份和恢复小知识
    Salesforce 导入导出数据简介
    GDPR 和个人信息保护的小知识
    代码审查要点简介
    Salesforce 外部对象
    Python scipy.sparse.csr_matrix()[csc_matrix()]
    Deep Compression
    Pascal voc api
    Python内置模块-xml模块
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15083470.html
Copyright © 2011-2022 走看看