poj1639经典题目啊 啊啊啊 啊啊 啊啊啊啊啊啊 啊啊啊啊
#include <iostream> #include<cstring> #include<string> #include<queue> #include<algorithm> #include<vector> #include<cstdio> #include<map> using namespace std; const int maxn=100+5; const int inf=0x3f3f3f3f; struct node { int u,v,w,next; bool flag; bool operator<(const node &s)const { return w<s.w; } node(){flag=false;} }; node E[200000],E1[20000],E2[20000]; //E用来存储已经构成树的边; E1用来存题目中存在的边; E2与vo相连的边 int vcnt,cnt,ecnt,now,n,k,vo; //vcnt与vo相连的边数,cnt出现的地点数,ecnt树种以相连的边数,now是指最初的m(即初始建树与vo相连的边数) bool ok; map<string ,int >mp; int pr[maxn]; int head[maxn];// int findd(int x)//并查集 { if(x==pr[x]) return x; else return pr[x]=findd(pr[x]); } void swapp(int &a,int &b)//两个数交换,使u存的是vo { int t; if(b!=vo) return; else { t=a;a=b;b=t; } } void init(int u,int v,int w)//建边 { E[ecnt].u=u; E[ecnt].v=v; E[ecnt].w=w; E[ecnt].flag=true; E[ecnt].next=head[u];//节点位置,将边连入原先建好的图中 head[u]=ecnt++; } void go()//将m棵最小生成树与vo相连 { int u,v; int x,y; now=0; for(int i=0;i<vcnt;i++) { u=E2[i].u; v=E2[i].v; // cout<<u<<" "<<v<<endl; x=findd(u); y=findd(v); // cout<<x<<" "<<y<<endl; if(x != y) { pr[x]=y; E2[i].flag=true; init(u,v,E2[i].w); init(v,u,E2[i].w); now++;//一定别忘了此处计数 m } } } int vis[maxn]; int pre[maxn];//保存路径 //bfs好好理解!!! int bfs(int s,int v,int ith,bool okk) //寻找当前图中s-v形成的环中的最大权值边(不与vo相连) { queue<int> q; while(q.size()) q.pop(); memset(vis,0,sizeof(vis)); memset(pre,-1,sizeof(pre)); vis[s]=1; q.push(s); while(q.size()) { int u=q.front();q.pop(); for(int i = head[u]; i != -1; i = E[i].next) { if(!E[i].flag)continue;//此边已经删去,不能考虑 int vv=E[i].v; if(!vis[vv]) { pre[vv]=i; vis[vv]=1; if(vv==v) break; q.push(vv); } } } int u=v; int id,res=0; while(1) { int i=pre[u]; //删掉环中与vo不关联的权值最大的边 if(E[i].u!=vo&&E[i].v!=vo&&res<E[i].w) { res=E[i].w; id=i; } u=E[i].u; if(u==s) break; } if(okk) { E[id].flag=E[id^1].flag=false;//删边^1的原因,E通常u和v互换成两个边,删的时候这两个应该都删掉 E2[ith].flag=true;//把与vo相连的边加入树中 init(s,v,E2[ith].w); init(v,s,E2[ith].w); } return E2[ith].w-res; //判断所求最大权值与当前vo边权大小关系(为负才删边加边) } void ff() { int id=-1,res =inf;//id-1 int u,v,w; //寻找最合适可加边 for(int i = 0; i < vcnt; i++) { u=E2[i].u; v=E2[i].v; w=E2[i].w; if(!E2[i].flag) { int tmp=bfs(vo,v,i,false);//此边的 if(tmp<res) { res=tmp; id=i; } } } if(res>=0)//说明已经再也没有更大的值 { ok=false;//剪枝 return; } bfs(vo,E2[id].v,id,true);//res<0 可以加边删边 } int get_ans() { int ans=0; for(int i=0;i<ecnt;i+=2) { if(E[i].flag)//此处不可少 ans+=E[i].w; } return ans; } void solve() { int u,v,w; memset(head,-1,sizeof(head)); sort(E1,E1+n); ecnt=0;vcnt=0; for(int i=0 ; i <= cnt ; i++) pr[i]=i; for(int i = 0;i < n ; i++) { u=E1[i].u; v=E1[i].v; w=E1[i].w; swapp(u,v); if(u == vo || v == vo )//将vo边单独存储 { E2[vcnt].u=u; E2[vcnt].v=v; //cout<<E2[vcnt].v<<endl; E2[vcnt].w=w; E2[vcnt].flag=false; vcnt++; continue;//注意! } int x,y; x=findd(u); y=findd(v); if(x != y) //建多个最小生成树(抠掉vo的) { pr[x]=y; init(u,v,w); init(v,u,w); } } go();//用vo将这多个最小生成树相连 ok=true; for(int i=now;i<k&&ok;i++)//由m到m+1的进化 ff(); int ans=get_ans();//计算权值 printf("Total miles driven: %d\n",ans); } int main() { cin>>n; string a,b; int ww; cnt=0; mp.clear(); for(int i = 0;i < n ;i ++) { cin>>a>>b>>ww; if (!mp[a]) mp[a] = ++cnt;//cnt++就错了。。。很奇怪 if (!mp[b]) mp[b] = ++cnt; E1[i].u = mp[a]; E1[i].v = mp[b]; E1[i].w = ww; } cin>>k; string c = "Park"; vo = mp[c]; solve(); return 0; } 累死我了。。。。
来源点击打开链接ll思路清晰,可惜他给的代码是错的,不过算法是引用的他的
代码点击打开链接
