1. 程式人生 > >POJ 1364 / HDU 3666 【差分約束-SPFA】

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

題意 最短 false nbsp DG bsp ont class ast

POJ 1364

題解:最短路式子:d[v]<=d[u]+w

式子1:sum[a+b+1]sum[a]>csum[a]<=sum[a+b+1]c1 — (a+b+1,a) c1

式子2:sum[a+b+1]sum[a]<csum[a+b+1]<=sum[a]+c1 — (a,a+b+1) c1

註意:先移項,移項完後再處理沒有等於的情況。

附加式: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連接,保證了圖的連通性,不需要新建邊。數據範圍註意,有nm個點和2nm條邊。註意剪枝

如果有起點,終點的約束,起點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;
}

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