因为一个木板可以切掉最多(m),所以可以先预处理哪些长度的木板可用,开个桶,然后对([l-m,l])打标记,再把打了标记的数取出来
假设可用长度(a_1,a_2,,,a_n)从小到大排好了序,我们可以先不用(a_1),只用后面的长度拼,然后用(a_1)凑,所以设(di_i)为能用后面的凑出的并且(mod a_1)为(i)的最短长度((di_0=0)),然后可以把(a_2,,,a_n)当做从位置(i)连向((i+a_j)mod a_1),边权为(a_j)的边,跑个最短路就可以求出所有(di)
最后答案为(max_{i=0}^{a_1-1}di_i-a_1),因为这些长度(d_i)没有用(a_1)拼出来,这里面任何一个长度减(a_1)就是不能拼出的长度
注意判无解
#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register
using namespace std;
const int N=3000+10,M=N*N;
il LL rd()
{
re LL x=0,w=1;re char ch;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
int to[M],nt[M],w[M],hd[N],tot=1;
il void add(int x,int y,int z) {++tot,to[tot]=y,nt[tot]=hd[x],w[tot]=z,hd[x]=tot;}
struct node
{
int x,d;
bool operator < (const node &b) const {return d>b.d;}
};
int n,m,a[N],di[N];
short vv[N];
il int gcd(int a,int b){return b?gcd(b,a%b):a;}
int main()
{
n=rd(),m=rd();
while(n--)
{
int x=rd();
++vv[max(1,x-m)],--vv[x+1];
}
n=0;
for(int i=1;i<=N-10;i++)
{
vv[i]+=vv[i-1];
if(vv[i]>0) a[++n]=i;
}
int gg=a[1];
for(int i=2;i<=n;i++) gg=gcd(gg,a[i]);
if(gg>1||a[1]==1) {puts("-1");return 0;} //判无解
for(int i=0;i<a[1];i++)
for(int j=2;j<=n;j++)
add(i,(i+a[j])%a[1],a[j]);
memset(di,63,sizeof(di));
di[0]=0;
priority_queue<node> q;
q.push((node){0,0});
while(!q.empty())
{
int x=q.top().x,d=q.top().d;
q.pop();
if(d>di[x]) continue;
for(int i=hd[x];i;i=nt[i])
{
int y=to[i];
if(di[y]>di[x]+w[i])
{
di[y]=di[x]+w[i];
q.push((node){y,di[y]});
}
}
}
int ans=a[1];
for(int i=0;i<a[1];i++) ans=max(ans,di[i]);
printf("%d
",ans-a[1]);
return 0;
}