版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/83419656
大意
给定每个点之间连接道路的限制 ,求出从1到 可以带走最大的区间,如果有多组解,输出字典序最小的一组
思路
首先很容易想到
#include<algorithm>
#include<vector>
#include<cstdio>
#define ri register int
using namespace std;int n,m,head[1001],tot,x,y,l,r,ansl,ansr;
struct node{int p,l,r;};
vector<node>e[6001];
bool vis[1001];
inline void dfs(ri x,ri L,ri R)//计算所有从1到n的路,求答案
{
if(R-L<ansr-ansl||(R-L==ansr-ansl&&L>=ansl)) return;//最优化剪枝
if(x==n)
{
if(R-L>ansr-ansl||R-L==ansr-ansl&&L<ansl)
{
ansl=L;
ansr=R;
}
return;
}
for(ri i=0;i<e[x].size();i++)
{
int y=e[x][i].p,xl=e[x][i].l,xr=e[x][i].r;
if(!vis[y]) {vis[y]=true;dfs(y,max(xl,L),min(xr,R));vis[y]=false;}
}
return;
}
signed main()
{
scanf("%d%d",&n,&m);
for(ri i=1;i<=m;i++)
{
scanf("%d%d%d%d",&x,&y,&l,&r);
e[x].push_back((node){y,l,r});
e[y].push_back((node){x,l,r});
}
vis[1]=true;dfs(1,1,1000000);
printf("%d\n",ansr-ansl+1);
for(ri i=ansl;i<=ansr;i++) printf("%d ",i);
}
于是愉快的 了
我们想到了一种固定边界的方法,假设我们枚举左边界,然后二分右边界,然后用一种算法判断是否可行(本人用的是并查集),但是这样的复杂度是 会
然后我们发现左右边界永远都是所有路中的,于是我们可以只枚举出现的左右边界,复杂度为 可以过
代码
#include<cstdio>
#include<algorithm>
#define ri register int
using namespace std;int f[1001],n,m,ans,ansl,l,r;
struct node{int l,r,u,v;}a[3001];
inline int find(ri x){return x==f[x]?x:f[x]=find(f[x]);}//并查集
inline bool cmp(node x,node y){return x.r<y.r;}//排序
inline void check(ri x)//判断是否可以到达
{
for(ri i=1;i<=n;i++) f[i]=i;
for(ri i=m;i>0;i--)
{
if(a[i].l>x) continue;//超出了我枚举的限制,就不走了
int u=find(a[i].u),v=find(a[i].v);
if(u!=v) f[u]=v;
if(find(1)==find(n))//可以到达
{
r=a[i].r;return;//因为我的r是排了序的,所以前面搜到的一定是最优的
}
}
r=-1000000000;return;//没找到
}
signed main()
{
scanf("%d%d",&n,&m);
for(ri i=1;i<=m;i++) scanf("%d%d%d%d",&a[i].u,&a[i].v,&a[i].l,&a[i].r);
sort(a+1,a+1+m,cmp);//排序
for(ri i=1;i<=m;i++)
{
l=a[i].l;
check(l);
if(r-l+1>ans||r-l+1==ans&&l<ansl)//比较
{
ans=r-l+1;
ansl=l;
}
}
printf("%d\n",ans);
for(ri i=ansl;i<ansl+ans;i++) printf("%d ",i);//输出
}