POJ 1364 / HDU 3666 【差分约束-SPFA】

时间:2024-10-16 19:08:08

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 连通所有点

 #include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std; const int N=;
const int INF=0x3f3f3f3f;
int n,m,cnt;
int head[N],d[N],Time[N];
bool vis[N]; struct edge{
int to,next,w;
}edge[N<<]; void add(int u,int v,int w){
edge[cnt].w=w;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;
} void init(){
cnt=;
memset(Time,,sizeof(Time));
memset(head,-,sizeof(head));
} bool SPFA(int st){
for(int i=;i<N;i++) d[i]=INF;
memset(vis,false,sizeof(vis));
queue <int> Q;
Q.push(st);
d[st]=;
vis[st]=true;
Time[st]=;
while(!Q.empty()){
int u=Q.front();
Q.pop();
vis[u]=false;
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].to;
if(d[v]>d[u]+edge[i].w){
d[v]=d[u]+edge[i].w;
if(!vis[v]){
Q.push(v);
vis[v]=true;
Time[v]++;
if(Time[v]>n) return false;
}
}
}
}
return true;
} int main(){
while(scanf("%d",&n)!=EOF&&n){
init();
scanf("%d",&m);
// 约束:s[0]<=s[i]+0
for(int i=;i<=n;i++) add(i,,);// 保证所有点连通
char op[];
for(int i=;i<=m;i++){
int a,b,c;
scanf("%d%d%s%d",&a,&b,&op,&c);
if(op[]=='g') add(a+b,a-,-c-);
else add(a-,a+b,c-);
}
if(SPFA()) printf("lamentable kingdom\n");
else printf("successful conspiracy\n");
}
return ;
}

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=+;
int n,m,cnt;
const double INF=1e12;
double l,r,c[N][N],d[N*];
int head[N*],Time[N*];
bool vis[N*]; struct e{
int to,next;
double w;
}edge[N*N*]; // 有反向边 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=;
memset(Time,,sizeof(Time));
memset(head,-,sizeof(head));
} bool SPFA(int s)
{
for(int i=;i<*N;i++) d[i]=INF;
memset(vis,, sizeof(vis));
queue<int> q;
q.push(s);
d[s]=;
vis[s]=;
Time[s]=;
while(q.size())
{
int u = q.front();q.pop();
vis[u]=;
for(int i=head[u];i!=-;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]=;
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=;i<=n;i++)
for(int j=;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()) cout<<"YES"<<endl; // 从连通的顶点开始
else cout<<"NO"<<endl;
}
return ;
}