POJ 1364 / HDU 3666 【差分约束-SPFA】
POJ 1364
题解:最短路式子:d[v]<=d[u]+w
式子1:sum[a+b+1]−sum[a]>c — sum[a]<=sum[a+b+1]−c−1 — (a+b+1,a) −c−1
式子2:sum[a+b+1]−sum[a]<c — sum[a+b+1]<=sum[a]+c−1 — (a,a+b+1) c−1
注意:先移项,移项完后再处理没有等于的情况。
附加式:sum[0]<=sum[i]+0 —— (i,0) 0 连通所有点
1 #include <queue> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 8 const int N=105; 9 const int INF=0x3f3f3f3f; 10 int n,m,cnt; 11 int head[N],d[N],Time[N]; 12 bool vis[N]; 13 14 struct edge{ 15 int to,next,w; 16 }edge[N<<1]; 17 18 void add(int u,int v,int w){ 19 edge[cnt].w=w;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++; 20 } 21 22 void init(){ 23 cnt=0; 24 memset(Time,0,sizeof(Time)); 25 memset(head,-1,sizeof(head)); 26 } 27 28 bool SPFA(int st){ 29 for(int i=0;i<N;i++) d[i]=INF; 30 memset(vis,false,sizeof(vis)); 31 queue <int> Q; 32 Q.push(st); 33 d[st]=0; 34 vis[st]=true; 35 Time[st]=1; 36 while(!Q.empty()){ 37 int u=Q.front(); 38 Q.pop(); 39 vis[u]=false; 40 for(int i=head[u];i!=-1;i=edge[i].next){ 41 int v=edge[i].to; 42 if(d[v]>d[u]+edge[i].w){ 43 d[v]=d[u]+edge[i].w; 44 if(!vis[v]){ 45 Q.push(v); 46 vis[v]=true; 47 Time[v]++; 48 if(Time[v]>n) return false; 49 } 50 } 51 } 52 } 53 return true; 54 } 55 56 int main(){ 57 while(scanf("%d",&n)!=EOF&&n){ 58 init(); 59 scanf("%d",&m); 60 // 约束:s[0]<=s[i]+0 61 for(int i=1;i<=n;i++) add(i,0,0);// 保证所有点连通 62 char op[10]; 63 for(int i=1;i<=m;i++){ 64 int a,b,c; 65 scanf("%d%d%s%d",&a,&b,&op,&c); 66 if(op[0]=='g') add(a+b,a-1,-c-1); 67 else add(a-1,a+b,c-1); 68 } 69 if(SPFA(0)) printf("lamentable kingdom\n"); 70 else printf("successful conspiracy\n"); 71 } 72 return 0; 73 }
HDU 3666
题解:由题意得:L<=c[i][j]∗a[i]/b[j]<=U 两边除以c[i][j]c[i][j] — L/c[i][j]<=a[i]/b[j]<=U/c[i][j],先两边取对数,得到log(L/c[i][j])<=log(a[i])−log(b[j])<=log(U/c[i][j]),推导出两个式子:
式子1:log(a[i])<=log(U/c[i][j])+log(b[j])
式子2:log(b[j])<=log(a[i])−log(L/c[i][j])
注意:log取double型,n个a和m个b连接,保证了图的连通性,不需要新建边。数据范围注意,有n∗m个点和2∗n∗m条边。注意剪枝
如果有起点,终点的约束,起点d[]距离就赋值为0,其余赋值为无穷。而对于没有起点,终点的约束,全部d[]距离都赋值为无穷。spfa算法,把所有点一开始都入队,这样每个点都遍历到了,就能保证不会有负环由于图的不连通而不被找到。差分约束能把所有约束条件转换成边求最短路,判断负环来解决问题。
#include <cmath> #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N=400+10; int n,m,cnt; const double INF=1e12; double l,r,c[N][N],d[N*2]; int head[N*2],Time[N*2]; bool vis[N*2]; struct e{ int to,next; double w; }edge[N*N*2]; // 有反向边 void add(int u,int v,double w){ edge[cnt].w=w;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++; } void init(){ cnt=0; memset(Time,0,sizeof(Time)); memset(head,-1,sizeof(head)); } bool SPFA(int s) { for(int i=0;i<2*N;i++) d[i]=INF; memset(vis,0, sizeof(vis)); queue<int> q; q.push(s); d[s]=0; vis[s]=1; Time[s]=1; while(q.size()) { int u = q.front();q.pop(); vis[u]=0; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].to; double w=edge[i].w; if(d[v]>d[u]+w) { d[v]=d[u]+w; if(!vis[v]) { q.push(v); vis[v]=1; if(++Time[v]>sqrt(n+m))return false; } } } } return true; } int main(){ while(scanf("%d%d%lf%lf",&n,&m,&l,&r)!=EOF){ init(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf("%lf",&c[i][j]); add(j+n,i,log(r/c[i][j])); add(i,j+n,log(c[i][j]/l)); } if(SPFA(1)) cout<<"YES"<<endl; // 从连通的顶点开始 else cout<<"NO"<<endl; } return 0; }