XJOI xtx的旅行(状压+spfa)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XJOI xtx的旅行(状压+spfa)相关的知识,希望对你有一定的参考价值。

技术分享

技术分享

技术分享

技术分享

这题是一道状压加spfa,考试的时候我非常naive地写了一个复杂度为O(4^p)*O(n+m)的暴力,因为复杂度上界比较松,就卡着评测机水过了.(2000+ms)

但是这题仅仅那样做非常不科学,非常慢,也可以被卡掉时间.

于是,我请教了旁边的cxt(%%%%)

cxt对我说到达一个点以及一个状态后,可以分开买物品和走路,这非常有道理,因为买物品和走路是互不影响的,这样上界就是O(2^p)*O(n+m)

于是,我贯彻cxt的思想,写了一个自认为很高妙的程序,跑了500ms+.

但是,cxt看了我的程序,认为我还是很不对,因为可以一个一个买物品,而不需要枚举子集,因为存下了买物品的状态后,一个一个买物品时相互之间也已经互不影响了.

于是,我又写了一个100ms+的程序

以下依次是三个程序:

技术分享
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=1e16,N=210,MAX_ZT=1050,M=2010,P=11;
int n,m,p,s,x,y,z,hh,max_zt,k,co[1034],b[M],c[M],d[M],fi[N],ne[M],have[N],price[N][P];
ll f[N][MAX_ZT];
bool bb[N][MAX_ZT];
struct dd{int ind,zt;dd(int x,int y):ind(x),zt(y){}};
queue<dd>q;
inline void add(int x,int y,int z,int hh){
    b[++k]=y; c[k]=z; d[k]=hh; ne[k]=fi[x]; fi[x]=k;
}
inline int count(int x){
    if (co[x]) return co[x];
    int res=0,yx=x;
    for ( ; x; x>>=1) if (x&1) ++res;
    return co[yx]=res;
}
inline int calc(int x,int y){
    int gi=1,res=0;
    for (int i=0; i<p; i++){
        if (y&gi) res+=price[x][i+1];
        gi<<=1;
    }
    return res;
}
void spfa(){
    for (int i=1; i<=n; i++)
    for (int j=0; j<max_zt; j++)
    f[i][j]=INF;
    for (int l=have[s]; l>=0; l=(l>0?have[s]&(l-1):-1)) f[s][l]=calc(s,l),q.push(dd(s,l));
    while (!q.empty()){
        int x=q.front().ind; 
        int k=q.front().zt;
        q.pop(); bb[x][k]=0;
        for (int j=fi[x]; j; j=ne[j]){
            int cost_on_road=c[j]+count(k)*d[j];
            for (int l=have[b[j]]; l>=0; l=(l>0?have[b[j]]&(l-1):-1))
                if ((!(k&l))&&f[x][k]+cost_on_road+calc(b[j],l)<f[b[j]][k|l]){
                    f[b[j]][k|l]=f[x][k]+cost_on_road+calc(b[j],l);
                    if (!bb[b[j]][k|l]) q.push(dd(b[j],k|l)),bb[b[j]][k|l]=1;
                }
        }
    }
}
int main(){
    scanf("%d%d%d%d",&n,&m,&p,&s);
    for (int i=1; i<=p; i++){
        scanf("%d",&k);
        for (int j=1; j<=k; j++){
            scanf("%d%d",&x,&y);
            if (!price[x][i]||y<price[x][i]) price[x][i]=y;
            have[x]|=1<<(i-1);
        }
    }
    for (int i=1; i<=m; i++) scanf("%d%d%d%d",&x,&y,&z,&hh),add(x,y,z,hh);
    max_zt=(1<<p);
    spfa();
    printf("%lld",f[s][max_zt-1]);
}
View Code
技术分享
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=1e16,N=210,MAX_ZT=1050,M=2010,P=11;
int n,m,p,s,x,y,z,hh,max_zt,k,co[1034],b[M],c[M],d[M],fi[N],ne[M],have[N],price[N][P],ca[N][MAX_ZT];
ll f[N][MAX_ZT];
bool bb[N][MAX_ZT];
struct dd{int ind,zt;dd(int x,int y):ind(x),zt(y){}};
queue<dd>q;
inline void add(int x,int y,int z,int hh){
    b[++k]=y; c[k]=z; d[k]=hh; ne[k]=fi[x]; fi[x]=k;
}
inline int calc(int x,int y){
    if (ca[x][y]) return ca[x][y];
    int gi=1,res=0;
    for (int i=0; i<p; i++){
        if (y&gi) res+=price[x][i+1];
        gi<<=1;
    }
    return ca[x][y]=res;
}
void spfa(){
    for (int i=1; i<=n; i++)
    for (int j=0; j<max_zt; j++)
    f[i][j]=INF;
    f[s][0]=0;
    q.push(dd(s,0));
    while (!q.empty()){
        int x=q.front().ind; 
        int k=q.front().zt;
        q.pop(); bb[x][k]=0;
        for (int j=have[x]; j; j=have[x]&(j-1))
        if (!(k&j)&&f[x][k|j]>f[x][k]+calc(x,j)){
            f[x][k|j]=f[x][k]+calc(x,j);
            if (!bb[x][k|j]) q.push(dd(x,k|j)),bb[x][k|j]=1;
        }
        for (int j=fi[x]; j; j=ne[j]){
            int cost_on_road=c[j]+co[k]*d[j];
            if (f[b[j]][k]>f[x][k]+cost_on_road){
                f[b[j]][k]=f[x][k]+cost_on_road;
                if (!bb[b[j]][k]) q.push(dd(b[j],k)),bb[b[j]][k]=1;
            }    
        }
    }
}
int main(){
    scanf("%d%d%d%d",&n,&m,&p,&s);
    for (int i=1; i<=p; i++){
        scanf("%d",&k);
        for (int j=1; j<=k; j++){
            scanf("%d%d",&x,&y);
            if (!price[x][i]||y<price[x][i]) price[x][i]=y;
            have[x]|=1<<(i-1);
        }
    }
    for (int i=1; i<=m; i++) scanf("%d%d%d%d",&x,&y,&z,&hh),add(x,y,z,hh);
    max_zt=(1<<p);
    co[0]=0; for (int i=1; i<max_zt; i++) co[i]=co[i&(i-1)]+1;
    spfa();
    printf("%lld",f[s][max_zt-1]);
}
View Code
技术分享
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=1e16,N=210,MAX_ZT=1050,M=2010,P=11;
int n,m,p,s,x,y,z,hh,max_zt,k,co[1034],b[M],c[M],d[M],fi[N],ne[M];
ll f[N][MAX_ZT];
bool bb[N][MAX_ZT];
struct dd{int ind,zt;dd(int x,int y):ind(x),zt(y){}};
vector<dd>price[N];
queue<dd>q;
inline void add(int x,int y,int z,int hh){
    b[++k]=y; c[k]=z; d[k]=hh; ne[k]=fi[x]; fi[x]=k;
}
void spfa(){
    for (int i=1; i<=n; i++)
    for (int j=0; j<max_zt; j++)
    f[i][j]=INF;
    f[s][0]=0;
    q.push(dd(s,0));
    while (!q.empty()){
        int x=q.front().ind; 
        int k=q.front().zt;
        q.pop(); bb[x][k]=0;
        for (vector<dd>::iterator it=price[x].begin(); it!=price[x].end(); it++){
            dd t=*it;
            int l=1<<t.ind-1;
            if (!(k&l)&&f[x][k|l]>f[x][k]+t.zt){
                f[x][k|l]=f[x][k]+t.zt;
                if (!bb[x][k|l])  q.push(dd(x,k|l)),bb[x][k|l]=1;
            }
        }
        for (int j=fi[x]; j; j=ne[j]){
            int cost_on_road=c[j]+co[k]*d[j];
            if (f[b[j]][k]>f[x][k]+cost_on_road){
                f[b[j]][k]=f[x][k]+cost_on_road;
                if (!bb[b[j]][k]) q.push(dd(b[j],k)),bb[b[j]][k]=1;
            }
        }
    }
}
int main(){
    scanf("%d%d%d%d",&n,&m,&p,&s);
    for (int i=1; i<=p; i++){
        scanf("%d",&k);
        for (int j=1; j<=k; j++){
            scanf("%d%d",&x,&y);
            price[x].push_back(dd(i,y));
        }
    }
    for (int i=1; i<=m; i++) scanf("%d%d%d%d",&x,&y,&z,&hh),add(x,y,z,hh);
    max_zt=(1<<p);
    co[0]=0; for (int i=1; i<max_zt; i++) co[i]=co[i&(i-1)]+1;
    spfa();
    printf("%lld",f[s][max_zt-1]);
}
View Code

 

以上是关于XJOI xtx的旅行(状压+spfa)的主要内容,如果未能解决你的问题,请参考以下文章

XJOI 旅行(树形DP)

旅行商问题状压DP

P2622 关灯问题II(状压&spfa)

POJ2686 Traveling by Stagecoach(状压DP+SPFA)

bzoj 1556: 墓地秘密状压dp+spfa

XJOI 修缮计划(最小生成树,LCA)