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();
    	}
    }
    
  • 相关阅读:
    es集群状态
    浅谈GO语言中的面向对象
    jstat命令详解
    jvm g1gc回收器
    解决ES集群状态异常教程(存在UNASSIGNED)
    html5分割上传实现超大文件无插件网页上传工具
    html5分割上传实现超大文件无插件网页上传
    科讯使用的:ckeditor编辑器.复制word图片.一直沾不上去.谁有好的解决办法呢
    编辑器直接word直接上传word里的图片
    请问有支持直接从 word 文档复制图片的 editor 吗
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14653420.html
Copyright © 2011-2022 走看看