luogu P2737

题目链接

题意,给一些数,问最大的不能被这些数表示出来的数(自由相加)是多少。(大凯的疑惑)

解法

一个想法是dp,然后对答案有影响的上界接近于最大数的平方。
这个和noip 2018 d1t2也差不多

另一个做法是先选择这些数中的一个数,我们为了方便就选最小的一个mn,然后可以发现如果可以表示出x,那么 x + m n , x + 2 × m n , x + 3 × m n x+mn,x+2\times mn,x+3\times mn x+mn,x+2×mn,x+3×mn等都是可以被表示出来的,所以我们将其它的数按对mn取模的值分类。这样就可以对[0,mn-1]这里面的每个数 i i i 计算最小的可以被原数列表示出来的数,同时对 m n mn mn 取模等于 i i i .然后再这些最小值里取一个最大的,就是答案了。
然后怎么求这些最小值呢,这里可以使用最短路的方法,将每个数看成一条边进行转移(这里和dp有点像)。

#include<bits/stdc++.h>
using namespace std;
const int maxn=10005;
inline int read(){
	char c=getchar();int t=0,f=1;
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n,m,a[maxn],mn,alfa[maxn],cnt,mp[maxn];
typedef pair<int,int> pii;
vector<pii> e[maxn];
inline void add(int a,int b,int c){
	e[a].push_back(pii(b,c));
}
priority_queue<pii,vector<pii>,greater<pii> > q;
int dis[maxn],vis[maxn];
const int inf=0x3f3f3f3f;
void dj(){
	for(int i=1;i<mn;i++){dis[i]=-1;}
	q.push(pii(0,0));
	while(!q.empty()){
		int u=q.top().second;q.pop();
		if(vis[u])continue;
	//	while(vis[u]){u=q.top().second;q.pop();}
		vis[u]=1;
		pii v;int l=e[u].size();
		for(int i=0;i<l;i++){
			v=e[u][i];//printf("%d %d\n",u,v.first);
			if((dis[v.first]==-1)||(dis[v.first]>dis[u]+v.second)){
				dis[v.first]=dis[u]+v.second;
				q.push(pii(dis[v.first],v.first));
			}
		}
	}
}
int main(){
	//freopen("8.in","r",stdin);
	//freopen("8.out","w",stdout);
	n=read();//m=read();
	mn=0x3f3f3f3f;
	for(int i=1;i<=n;i++){
		a[i]=read();
	//	for(int j=a[i];j>=max(1,a[i]-m);j--){mp[j]=1;}
	}
/*	for(int i=1;i<=3000;i++){
		if(mp[i]){alfa[++cnt]=i;}
	}
	n=cnt;mn=3001;
	for(int i=1;i<=n;i++){
		a[i]=alfa[i];
		//printf("%d\n",a[i]);
	}*/
	for(int i=1;i<=n;i++){
		if(mn>a[i])mn=a[i];
	}
//	printf("%d\n",mn);
	for(int i=0;i<mn;i++){
		for(int j=1;j<=n;j++){
	//		printf("%d %d\n",i,(i+a[j])%mn);
			add(i,(i+a[j])%mn,a[j]);
		}
	}
	dj();
	int mx=0,flag=1;
	for(int i=0;i<mn;i++){
		if((dis[i]==-1)||(dis[i]-mn>inf)){flag=0;break;}
	//	printf("%d %d\n",i,dis[i]);
		mx=max(mx,dis[i]);
	}
	if(!flag){puts("0");}
	else{
		printf("%d\n",max(mx-mn,0));
	}
	return 0;
}

(这里有些注释的代码是因为这本来是另一道题,但是双倍经验了)
这个的复杂度 O ( n ∗ m i n ( a [ i ] ) + n l o g ( n ) ) O(n*min(a[i])+nlog(n)) O(nmin(a[i])+nlog(n)),比dp快很多

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值