差分约束系统
两天搞了一下差分约束,其实就是一堆不等式进而来求解。
1.建图并求解
2.x>y---> x>=y+1 表示从y到x连一条边 边权为1
3.x<y -----> y>x ------> y>=x+1 表示从x到y连一条边 边权为1
输出 2
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<ctime> #include<algorithm> #include<iomanip> #include<map> #include<string> #include<stack> #include<queue> #include<vector> #include<cmath> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int maxn=17000; int lin[maxn],e[maxn],ver[maxn],nex[maxn],len=0; int vis[maxn],flag=0,dis[maxn],q[maxn],h=0,t=1;; void add(int x,int y,int z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } int n,m; int s1=1002,s2=1003,u[maxn]; void spfa(int x) { memset(vis,0,sizeof(vis)); memset(dis,-10,sizeof(dis)); memset(q,0,sizeof(q)); memset(u,0,sizeof(u)); q[t]=x;dis[x]=0;vis[x]=1; while(h<=t) { int tn=q[++h];vis[tn]=0; for(int i=lin[tn];i;i=nex[i]) { int tmp=ver[i]; if(dis[tn]+e[i]>dis[tmp]) { dis[tmp]=dis[tn]+e[i]; if(vis[tmp]!=1) { q[++t]=tmp; vis[tmp]=1; u[tmp]++; } } if(u[tmp]>=n+2){printf("NO\n");flag=1;return;} } } } int main() { //freopen("1.in","r",stdin); n=read();m=read(); memset(vis,0,sizeof(vis)); for(int i=1;i<=m;i++) { int x,y,z; x=read();y=read();z=read(); if(z==1)add(x,y,1); if(z==-1)add(y,x,1); if(z==0)add(x,y,0),add(y,x,0); } for(int i=1;i<=n;i++)add(s1,i,0),add(i,s2,0); spfa(s1);if(flag==1)return 0; printf("%d\n",dis[s2]); return 0; }
下面一道我调了一中午还超时的差分约束。
裸的建图然后spfa跑最短(最长路)+判环,下面我跑的的最长路。
#include<bits/stdc++.h> #include<cstdio> #include<cstring> #include<string> #include<ctime> #include<cmath> #include<iostream> #include<iomanip> #include<vector> #include<stack> #include<map> #include<queue> #include<algorithm> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int maxn=30020; int n,m,ans=0,h=0,t=0,flag=0,len=0; int lin[maxn<<1],nex[maxn<<1],ver[maxn<<1],e[maxn<<1],u[maxn+10]; int dis[maxn],vis[maxn+10],s1=maxn,s2=maxn+1,q[maxn<<3]; void add(int x,int y,int z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } void spfa(int x) { memset(dis,-1,sizeof(dis)); memset(vis,0,sizeof(vis)); q[++t]=x;vis[x]=1;dis[x]=0; while(h++<t) { int te=q[h];vis[te]=0; for(int i=lin[te];i;i=nex[i]) { int tn=ver[i]; if(dis[tn]<dis[te]+e[i]) { dis[tn]=dis[te]+e[i]; if(vis[tn]==0){vis[tn]=1;q[++t]=tn;} u[tn]++; } if(u[tn]>n){flag=1;return;} } } } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=m;i++) { int x,y; x=read();y=read(); add(y,x,1); } for(int i=1;i<=n;i++)add(s1,i,0),add(i,s2,0); spfa(s1); if(flag==1)printf("impossible\n"); else { for(int i=1;i<=n;i++)ans+=dis[i]+100; printf("%d\n",ans); } return 0; }
上面是超时的一个代码,wa掉一个点因为判环的时候效率不高,n过大,那么如果是一个很大很大的环的话,一定会超时。所以这里选择了双端队列优化,不知道是不是人品的原因,每次双端队列优化都能使让我的超时代码过掉。~高效
#include<bits/stdc++.h> #include<cstdio> #include<cstring> #include<string> #include<ctime> #include<cmath> #include<iostream> #include<iomanip> #include<vector> #include<stack> #include<map> #include<queue> #include<algorithm> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int maxn=30020; int n,m,ans=0,flag=0,len=0; int lin[maxn<<1],nex[maxn<<1],ver[maxn<<1],e[maxn<<1],u[maxn+10]; int dis[maxn],vis[maxn+10],s1=maxn,s2=maxn+1; deque<int>q; void add(int x,int y,int z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } void spfa(int x) { memset(dis,-1,sizeof(dis)); memset(vis,0,sizeof(vis)); q.push_back(x);vis[x]=1;dis[x]=0; while(q.size()!=0) { int te=q.front();vis[te]=0;q.pop_front(); for(int i=lin[te];i;i=nex[i]) { int tn=ver[i]; if(dis[tn]<dis[te]+e[i]) { dis[tn]=dis[te]+e[i]; if(vis[tn]==0) { if(q.size()!=0&&dis[ver[i]]<=dis[q.front()])q.push_back(ver[i]); else q.push_front(ver[i]); vis[tn]=1; } u[tn]++; } if(u[tn]>n){flag=1;return;} } } } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=m;i++) { int x,y; x=read();y=read(); add(y,x,1); } for(int i=1;i<=n;i++)add(s1,i,0),add(i,s2,0); spfa(s1); if(flag==1)printf("impossible\n"); else { for(int i=1;i<=n;i++)ans+=dis[i]+100; printf("%d\n",ans); } return 0; }
严密分析一中午发现上面超时原因是判环速度过慢,云学长是采用了拓扑类型的判环,效率会高一点,但其实这道题直接dfs找环加spfa求一个全局最长路即可,但值得注意的是dij+堆是求不出最长路的,dij+堆的原理就是全局最小值不可能被更新,而全局最大值有可能被更新故不能求最长路!
#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<cstdio> #include<cmath> #include<cstring> #include<string> #include<stack> #include<queue> #include<algorithm> #include<vector> #include<map> #include<ctime> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } queue<int>q; const int maxn=200200; int n,m,s1=maxn-10,flag=0,ans=0; int lin[maxn<<1],ver[maxn<<1],nex[maxn<<1],e[maxn<<1],len=0; int vis[maxn<<1],dis[maxn<<1]; void add(int x,int y,int z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } void dfs(int x) { vis[x]=1; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(vis[tn]==1){flag=1;return;} dfs(tn); } vis[x]=0; return; } void spfa(int x) { memset(dis,-1,sizeof(dis)); memset(vis,0,sizeof(vis)); q.push(x);vis[x]=1;dis[x]=0; while(q.size()!=0) { int te=q.front();vis[te]=0;q.pop(); for(int i=lin[te];i;i=nex[i]) { int tn=ver[i]; if(dis[tn]<dis[te]+e[i]) { dis[tn]=dis[te]+e[i]; if(vis[tn]==0) { vis[tn]=1; q.push(tn); } } } } } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=m;i++) { int x,y; x=read();y=read(); add(y,x,1); } for(int i=1;i<=n;i++)add(s1,i,0); memset(vis,0,sizeof(vis));dfs(s1); if(flag==1){printf("impossible");return 0;} spfa(s1); for(int i=1;i<=n;i++)ans+=dis[i]+100; printf("%d\n",ans); return 0; }
散落至四方。又或是升起至四方。在无法确认这一事实的浮游感中,彗星耀辉在夜空。彗星裂开,碎片落下。 ——你的名字