vijos 1054 牛场围栏 【想法题】
这题刚看完后第一个想到的方法是背包 但仔细分析数据范围后会发现这题用背包做复杂度很高
比如对于这样的数据
2 100
2999
2898
(如果有神犇可以用背包过掉这样的数据 请回复下背包的做法)
-----------------------------------------------------------------------------------
在翻看了vijos上自带题解区后 会发现有些人提到了最短路
设最后可用木料中最短的长度为$L0$ 则显然若长度X可以得到 那么长度$X+L0$也可以得到
所以我们可以研究下在$modL0$的意义下 每种长度至少要为多长可以得到
设所有可得到的长度最小值中的最大值为$Y$ 那么$Y-L0$即为最后的答案
由于建图已经是$O(L^2)$了 因此求最短路的时候直接写不加堆优化的dijkstra总复杂度也是$O(L^2)$的
具体实现以及细节处理可参考代码
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define rep(i,n) for(int i=1;i<=n;++i) #define imax(x,y) (x>y?x:y) #define imin(x,y) (x<y?x:y) using namespace std; const int L=3010; int firste[L],nexte[L*L],v[L*L],w[L*L]; int dist[L],num[L]; bool color[L],used[L]; int n,m,e=1,low=2147483647,top=0,cnt=0; bool flag=0; void build_edge(int x,int y,int z) { ++e; nexte[e]=firste[x]; firste[x]=e; v[e]=y; w[e]=z; } int gcd(int x,int y) { if(!y)return x; return gcd(y,x%y); } bool check(int x) { if(x==low)return 0; int tx=x%low; while(tx<x) { if(color[tx])return 0; tx+=low; } return 1; } int main() { int x,ans=0; scanf("%d%d",&n,&m); rep(i,n) { scanf("%d",&x); if(x-m<=1) { printf("-1"); return 0; } low=imin(low,x-m); top=imax(top,x); for(int j=x-m;j<=x;++j) color[j]=1; } for(int i=2;i<=top;++i) if(color[i]) num[++cnt]=i; for(int i=1;i<cnt;++i) { for(int j=i+1;j<=cnt;++j) if(gcd(num[i],num[j])==1) { flag=1; break; } if(flag)break; } if(!flag) { printf("-1"); return 0; } rep(i,cnt) if(check(num[i])) { int z=num[i]/low; for(int j=0;j<low;++j) { int y=(j+num[i])%low; if(y>j) build_edge(j,y,z); else build_edge(j,y,z+1); } } memset(dist,60,sizeof(dist)); dist[0]=0; for(int i=1;i<low;++i) { int u=low; for(int j=0;j<low;++j) if(!used[j]&&dist[j]<dist[u])u=j; used[u]=1; for(int p=firste[u];p;p=nexte[p]) if(dist[v[p]]>dist[u]+w[p]) dist[v[p]]=dist[u]+w[p]; } for(int i=0;i<low;++i) ans=imax(ans,(dist[i]-1)*low+i); printf("%d",ans); return 0; }
另外 vijos1065 的思路也与此类似 只不过更加直接了