今天算法设计与分析课上布置了一道BFS,感觉还是挺有意思的。
估摸着放到CF上这道题应该能有1700左右吧qwq
拿来和大家分享一下
题目大家自己看一看,我来解释一下样例。
样例输入,3就代表有3个元件,第二行表示元件1和2之间要接2根线,元件1和3之间要接3根线,第三行表示元件2和3之间要接3根线。
样例输出,10代表最小成本是10(可以算,成本是1*3+2*2+1*3=10, 可以证明这是最小的),第二行表示这种可以达到最小成本的布线方式。
这题怎么做呢?
我们的node(答案集)里面应该包括三个东西,即现在已经有的点,现在已经有的点所拥有的布线成本,和这几个点的位置分布(数组形式)。既然要用优先队列,那我们就得先重载一下小于运算符。
这道题应该是一个搜索题,我们使用BFS进行 ,由于数据量为20且要求排列,复杂度可能高达阶乘级别,这样的复杂度是难以通过的。我们需要进行剪枝,具体的剪枝方案如下:
①当正在扩展的节点的成本已经超过目前的最优解,那么就直接break掉(即不把这个扩展节点扔进队列)
②当当前节点的成本已经超过了目前的最优解,说明下面无论如何放置成本都不会更低,可以直接break出循环。
这样的话会让复杂度得到一个较大的缓解。可以在比较短的时间内通过这道题。
其实有一说一,不是很能理解为什么这题一定要用优先队列来做。普通队列毕竟少一个log的复杂度。
不过这样优先拿成本小的情况出来处理,确实会导致更多的剪枝操作。可能优先队列的平均复杂度更低吧()
代码如下:
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f bool ispow(ll n){return (n&(n-1))==0;} #define lowbit(x) (x&(-x)) const int mod=1e9+7; int con[25][25]; int anselem[25]; int n; struct node{ int num; int cost; int member[25]; bool operator<(const node& a)const{ return cost<a.cost; } }a[25]; int cal(node nn,int curnum){ int res=0; for(int i=1;i<=curnum-1;i++){ for(int j=i+1;j<=curnum;j++) res+=(abs(j-i)*con[nn.member[i]][nn.member[j]]); } return res; } int solve(){ priority_queue<node>q; node cur; cur.num=0,cur.cost=0; for(int i=1;i<=n;i++) cur.member[i]=i; q.push(cur); int minn=inf; while(!q.empty()){ cur=q.top(); q.pop(); if(cur.cost>=minn) break; if(cur.num==n-1){ int temp=cal(cur,n); if(temp<minn){ minn=temp; for(int i=1;i<=n;i++) anselem[i]=cur.member[i]; } } else{ for(int i=cur.num+1;i<=n;i++){ node nxt; nxt.num=cur.num+1; for(int j=1;j<=n;j++) nxt.member[j]=cur.member[j]; nxt.member[nxt.num]=cur.member[i]; nxt.member[i]=cur.member[nxt.num]; nxt.cost=cal(nxt,nxt.num); if(nxt.cost<minn) q.push(nxt); } } } return minn; } int main() { fast; cin>>n; for(int i=1;i<=n-1;i++) for(int j=i+1;j<=n;j++) { int r; cin>>r; con[i][j]=r; con[j][i]=r; } int ans=solve(); cout<<ans<<'\n'; // node r; // for(int i=1;i<=n;i++) r.member[i]=anselem[i]; // cout<<cal(r,n)<<'\n'; for(int i=1;i<=n;i++) cout<<anselem[i]<<' '; return 0; }
老师当时给的样例不完整,然后我就去到处找样例数据。最后竟然还真在一个博主的博客下面找到了这组数据
Reference:https://blog.csdn.net/ioio_/article/details/81176845
大家可以拿数据自己测试。(上面倒数后几行被注释掉的代码,就是相当于一个spj,可以拿那个检查自己的排列是否正确)
[Test #1] Input: 3 2 3 3 Output: 10 1 3 2 [Test #2] Input: 9 18 1 10 18 10 14 8 4 17 8 10 16 5 12 2 11 0 7 14 19 16 6 17 2 18 18 6 0 8 9 7 11 14 13 6 17 Output: 964 2 3 8 5 1 4 9 6 7 [Test #3] Input: 9 2 6 5 10 4 19 0 0 6 19 7 12 5 1 18 12 8 14 0 10 13 1 5 18 5 14 7 4 17 7 13 3 7 18 14 5 Output: 804 1 9 6 8 2 5 4 3 7
那就到这里吧,这几天要赶大作业了。
坦克大战好难啊qwqqq