hdu 6166 Senior Pan(SPFA+二進制分組)

題目地址:http://acm.hdu.edu.cn/showproblem.php?pid=6166

思路:

1.從集合A中一點到集合B中一點的最短路徑:設立源點0,連向所有A集合中的點,邊權值爲0;設立匯點,B集合中點向其連邊,邊權值爲0。求源點到匯點的最短路即爲集合A到集合B的最短路。

2.將最短兩點分別分到A、B集合:由於圖中任意兩點標號不同,其二進制表示中必有至少一位不同。按二進制枚舉第0--->17位,若u與v該位不同,則u分到集合A,v分到集合B,求A到B最短路和B到A最短路取最小。

3.由於不同u與v必定存在至少一位不同,所以每個u與每個v都兩兩分到不同集合(即包含所有組合)其中必有至少一次u與v分到正確集合,此時答案即爲最小。

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug
using namespace std;
const int maxn=1e5+50;
const int INF=0x3f3f3f3f;
struct Edge
{
    int to,nt,w;
};
int n,m,tot;
int a[maxn];
queue<int> q;
int x[maxn],y[maxn],w[maxn];
int head[maxn*10],flag[maxn];
int vis[maxn],dist[maxn];
Edge edge[maxn*10];
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
}
void addEdge(int u,int v,int w)
{
    edge[tot].to=v,edge[tot].nt=head[u];
    edge[tot].w=w,head[u]=tot++;
}
int mindist(int u)
{
    memset(vis,0,sizeof(vis));
    while(!q.empty()) q.pop();
    for(int i=0; i<=n+1; i++) dist[i]=INF;
    dist[u]=0,q.push(u),vis[u]=1;
    while(!q.empty())
    {
        int now=q.front();
        q.pop(),vis[now]=0;
        for(int i=head[now]; ~i; i=edge[i].nt)
        {
            int nt=edge[i].to;
            int w=edge[i].w;
            if(dist[nt]>dist[now]+w)
            {
                dist[nt]=dist[now]+w;
                if(!vis[nt])
                {
                    vis[nt]=1;
                    q.push(nt);
                }
            }
        }
    }
    return dist[n+1];
}
void build()
{
    init();
    for(int j=0; j<m; j++)
    {
        addEdge(x[j],y[j],w[j]);
    }
}
int main()
{
#ifdef debu
    freopen("in.txt","r",stdin);
#endif // debug
    int t,cas=0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&x[i],&y[i],&w[i]);
        }
        int k;
        scanf("%d",&k);
        for(int i=0; i<k; i++)
        {
            scanf("%d",&a[i]);
        }
        int ans=INF;
        for(int i=0; i<18; i++)
        {
            memset(flag,0,sizeof(flag));
            build();
            for(int j=0; j<k; j++)
            {
                if(a[j]&(1<<i))
                {
                    addEdge(0,a[j],0);
                    flag[a[j]]=1;
                }
                else
                {
                    addEdge(a[j],n+1,0);
                }
            }
            //cout<<"& "<<i<<" "<<mindist(0)<<endl;
            ans=min(ans,mindist(0));
            build();
            for(int j=0; j<k; j++)
            {
                if(flag[a[j]])
                {
                    addEdge(a[j],n+1,0);
                }
                else
                {
                    addEdge(0,a[j],0);
                }
            }
            //cout<<"* "<<i<<" "<<mindist(0)<<endl;
            ans=min(ans,mindist(0));
        }
        printf("Case #%d: %d\n",++cas,ans);
    }

    return 0;
}




發佈了381 篇原創文章 · 獲贊 6 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章