模拟赛20181016 Uva 1040 状压+搜索 2005 ACM world final problem c

2018/10/18 18:11
阅读数 19

题目的隐含条件将这道题指向了最小生成树;

利用类似prim的方法,枚举所有子图并判断是否包含询问点,如果包含那么可以更新答案;

边统计边更新,且由于更新一定是向更多的点状态下更新,所以一定可以统计到答案,不至于到全部是inf的情况

再更新答案时记录ps,pe两个变量分别表示此状态最后一次更新前的状态,边,会在寻找路径时用到

最后统计到的答案ans,走到初始的t,路径中打下vis标记后再从头dfs沿着vis打过的走下去,并在路径中遇到叶子节点时顺便将走过的路径放入vector

最后利用vector输出即可

#include<bits/stdc++.h>
#define rep(i,x,y) for(register int i=x;i<=y;i++)
#define dec(i,x,y) for(register int i=x;i>=y;i--)
using namespace std;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;}

const int N=25;
const int M=500;
const int inf=0x3f3f3f3f;

vector<int> v[N];
int n,t,m,q,x,all,ans,mx,ask[N];
int dp[1<<21],ps[1<<21],pe[1<<21];
bool vis[N];

int head[N],tot=1;
struct node{int v,w,next;bool vis;}e[N*N<<1];
void insert(int u,int v,int w){
    e[++tot]=(node){v,w,head[u],0};head[u]=tot;
    e[++tot]=(node){u,w,head[v],0};head[v]=tot;}
inline int count(int x){
    int ans=0;while(x) ans++,x-=x&(-x);return ans;}

int s[N],top;

void dfs(int u,int f){ s[++top]=u;
    if(vis[u]){
        dec(i,top,1) v[u].push_back(s[i]);}
        //将目前的记录顺序放入vector 
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].v;
        if(v==f||e[i].vis==0) continue;
        dfs(v,u);
    }--top;
}

int main(){ 
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    n=read(),t=read(),m=read();
    rep(i,1,m){int u=read(),v=read(),w=read();insert(u,v,w);}
    q=read();rep(i,1,q) x=read(),all|=(1<<(x-1)),vis[x]=1,ask[i]=x;
    //all 记录全部的询问点的集合 
    
    memset(dp,inf,sizeof dp);dp[1<<(t-1)]=0;ans=inf;
    //利用含t的集合更新答案,最后包含的集合一定有t 
    rep(i,0,(1<<n)-1){
        if((i&all)==all){//更新答案的集合包含 
            if(dp[i]<ans){
                ans=dp[i];mx=i;
            }else if(dp[i]==ans){
                if(count(i)<count(mx))//比较集合点数多少 
                mx=i;
            }continue;
        }
        for(int j=1;j<=n;j++)if((1<<(j-1))&i){
            for(int k=head[j];k;k=e[k].next){
                //沿着边更新答案 
                int v=e[k].v,w=e[k].w;
                if((1<<(v-1))&i) continue;
                if(dp[i|(1<<(v-1))]>dp[i]+w)
                   dp[i|(1<<(v-1))]=dp[i]+w,
                   ps[i|(1<<(v-1))]=i,pe[i|(1<<(v-1))]=k;
                //ps代表i上一个状态转移来的集合(点集),
                //pe代表 i上一次转移的边 
            }
        }
    }
    printf("distance = %d\n",ans);
    //利用已有标记爬回去 
    while(mx!=(1<<(t-1))) e[pe[mx]].vis=1,mx=ps[mx];
    dfs(t,0);//再正着下来,记录顺序,放入vector 
    rep(i,1,q){
        for(int j=0;j<v[ask[i]].size()-1;j++)
        printf("%d-",v[ask[i]][j]);
        printf("%d\n",v[ask[i]][v[ask[i]].size()-1]);}
    
    return 0;}

模拟赛搬的就是这道,本地A了但是提交UVA wa掉了,OJ实在太麻烦所以就咕咕咕了(喵喵喵),如果有ctrl c的同学可能会比较惨,不怪我了...haha

完结撒花

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部