zoukankan      html  css  js  c++  java
  • [NOI2020] 制作菜品

    一、题目

    点此看题

    二、解法

    独立思考确实挺有趣的,虽然花了很久时间但还是挺开心的。

    看到这题没什么思路,可以多看几遍题,发现关键条件 (n-2leq m),还是不怎么会,直接开部分分。

    (n-1=m) 怎么做啊?也就是原料数正好多一个,如果某个原料不足 (k),那么直接把他选完,再找到最大的原料选剩下的部分,这样可以一直递归下去,每次可以减少一个原材料,而且最后一定会剩下两个原材料和一次机会,这种情况一定有解,因为无论怎样最大的都可以让你凑足 (k)

    (n-1leq m) 都一样啊,略微改一下就行了,如果全部原料都大于等于 (k) 那么随便找一个减少 (k) 即可。


    发现现在只剩下 (n-2=m) 了,但是不能直接用 (n-1=m) 的方法,这样会出问题:最后可能会剩三个原材料,而且每次不一定就能减少一个原材料(最大值不够顶)。

    但是可以把它分成不相关的两个集合,每个集合用 (n-1=m) 的方法做即可,设划分的集合为 (S),那么有解当且仅当满足:(sum d_i=(|S|-1)cdot k),如果满足这个条件一定有解(充分性),现在考虑证明一下必要性:

    仔细观察我们的基础问题 (n-1=m),可以看成 (n-1) 条边联通了 (n) 个点,因为构造的过程反应到图上就是给每个点找父亲,(n-2=m) 就意味着至少有两个连通块,那一定会有至少一个连通块满足点数等于边数减一,我们把剩下的点看成另一个连通块即可,这两个连通块是独立的,可以适用于"树"的必要条件。

    现在的问题就是划分出一个满足 (sum d_i=(|S|-1)cdot k) 的集合了,用类似分数规划的技巧可以转化成 (sum d_i-k=-k),那么设 (dp[i][j]) 表示考虑前 (i) 个原料 (d_i-k) 的总和为 (j) 是否合法,可以用 ( t bitset) 优化这个 (01) 背包,时间复杂度 (O(Tfrac{n^2k}{w}))

    然后把 (dp) 路径还原一下就行了,所以除了一开始的结论剩下都是简单的技巧。

    #include <cstdio>
    #include <vector>
    #include <bitset>
    #include <set>
    using namespace std;
    const int M = 505;
    const int N = 2500000;
    #define fi first
    #define se second
    #define make make_pair
    #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 T,n,m,k,d[M],vis[M];vector<int> v;
    bitset<10000*M> dp[M];
    void solve()
    {
    	set<pii> s;
    	for(int i=0;i<v.size();i++)
    		s.insert(make(d[v[i]],v[i]));
    	while(!s.empty())
    	{
    		pii t1=*s.begin(),t2=*s.rbegin();
    		s.erase(t1);
    		while(t1.fi>=k)
    			t1.fi-=k,printf("%d %d
    ",t1.se,k);
    		if(!t1.fi) continue;
    		s.erase(t2);
    		int tmp=k-t1.fi;
    		printf("%d %d %d %d
    ",t1.se,t1.fi,t2.se,tmp);
    		t2.fi-=tmp;
    		s.insert(t2);
    	}
    	v.clear();
    }
    void devide()
    {
    	dp[0].reset();dp[0][N]=1;
    	for(int i=1;i<=n;i++)
    	{
    		dp[i]=dp[i-1];
    		if(d[i]>k)
    			dp[i]|=dp[i-1]<<(d[i]-k);
    		else
    			dp[i]|=dp[i-1]>>(k-d[i]);
    	}
    	if(!dp[n][N-k]) puts("-1");
    	else
    	{
    		int x=n,y=N-k;
    		while(x)
    		{
    			if(dp[x-1][y-(d[x]-k)])
    			{
    				y-=(d[x]-k);
    				vis[x]=1;
    				v.push_back(x);
    			}
    			x--;
    		}
    		solve();
    		for(int i=1;i<=n;i++)
    			if(!vis[i]) v.push_back(i);
    		solve();
    	}
    }
    signed main()
    {
    	T=read();
    	while(T--)
    	{
    		n=read();m=read();k=read();
    		for(int i=1;i<=n;i++)
    			d[i]=read(),vis[i]=0;
    		if(m!=n-2)
    		{
    			for(int i=1;i<=n;i++)
    				v.push_back(i);
    			solve();
    			continue;
    		}
    		devide();
    	}
    }
    
  • 相关阅读:
    Java实现 LeetCode 735 行星碰撞(栈)
    Java实现 LeetCode 735 行星碰撞(栈)
    Java实现 LeetCode 887 鸡蛋掉落(动态规划,谷歌面试题,蓝桥杯真题)
    Java实现 LeetCode 887 鸡蛋掉落(动态规划,谷歌面试题,蓝桥杯真题)
    Java实现 LeetCode 887 鸡蛋掉落(动态规划,谷歌面试题,蓝桥杯真题)
    Java实现 蓝桥杯算法提高 求最大值
    Java实现 蓝桥杯算法提高 求最大值
    Java实现 蓝桥杯算法提高 求最大值
    Python eval() 函数
    Python repr() 函数
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14653420.html
Copyright © 2011-2022 走看看