P7997

看着大佬们都用牛逼算法,蒟蒻表示不明白

Solution : dp

考虑dp,设 \(f_{i,j}\) 为做了 \(j\) 题的能力值为 \(i\) ,很容易可以得到状态转移方程
\(f_{i,j}=f_{i,j}|f_{i+a_k,j-1}|f_{i-a_k,j-1}\)

显然复杂度是 \(n\cdot max_M\) ,显然过不了。

通过打表我们发现在做 \(5\) 题及以内我也不知道具体是多少反正很小就对了就可以完成对能达成的能力值的覆盖,所以在完成覆盖后并不需要重复做,只需要记录当前的题数,大于等于这个题数就输出最大能力值就对了,其余的在动态规划过程中记录答案, \(O(1)\) 查询即可。

实测跑的就慢那么亿点点

#include<bits/stdc++.h>
#define re register
using namespace std;

const int N=4e3+1;
int n,a[2001],ans[N];
bool dp[N][2001];//dp[i][j]第 j 天的能力值为i 

inline long long read() { long long X=0; bool flag=1; char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();} while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}  if(flag) return X; return ~(X-1);}
inline void print(int X) { if(X<0) {X=~(X-1); putchar('-');} if(X>9) print(X/10); putchar(X%10+'0'); }


signed main()
{
	n=read(); int m=read(); int day=2005;
	for(re int i=1;i<=n;++i) a[i]=read();
	sort(a+1,a+1+n);
	dp[0][0]=true;
	if(n!=1)
	{
		for(re int j=1;j<=2000;++j)//第几天 
		{
			for(re int i=0;i<=a[n]*2-1;++i)//现在的能力
			{
				for(re int k=1;k<=n;++k)//第几套 
				{
					if(i+a[k]<4000) dp[i][j]=dp[i][j]|dp[i+a[k]][j-1];//是-得到的 
					if(i-a[k]<a[k]&&i-a[k]>=0) dp[i][j]=dp[i][j]|dp[i-a[k]][j-1];
					//是+得到的 
				}
			}
			int cnt=0;
			for(re int i=0;i<2*a[n];++i) if(dp[i][j]){ans[j]=i;++cnt;}
			if(cnt==a[n]*2) {day=j; break;}
		}
	}
	while(m--)
	{
		long long k=read();
		if(k>=day&&n!=1) cout<<a[n]*2-1<<endl;
		else
		{
			if(n==1)
			{
				print(k%2==1?a[1]:0); puts("");
			}
			else
			{
				print(ans[k]); puts("");	
			}
		}	
	}
	return 0;
}
/*
10
6 19 34 35 56 63 82 82 83 99
*/
posted @ 2022-05-08 09:27  starrylasky  阅读(19)  评论(0)    收藏  举报