牛站(貪心+暴力做法)
阿新 • • 發佈:2020-09-14
題目
做法
目前看下來貌似只有我的做法最優秀,是\(O(n^3)\)
當然,\(n=101\),首先,點的個數最多是邊的個數加一,這個很好理解。
我們開始思考,走這麼多邊,肯定是要走環的,對吧,那麼有一個假想,如果他就是在一條最短路上的最小邊反覆橫跳呢?
我們用“表面否的演算法”(Bellman - Ford演算法)處理起點和終點到達各個點在走過\(k\)條邊時的最短路,當然\(k\)最大為多少呢?,我們假定這條路徑就在這裡反覆橫跳,不存在其餘的環,那麼\(k\)最大就為點的個數,但是,其實是有可能存在其他環的,因為我們這樣來回彈跳,只會造成偶數環,那麼如果剛好找個奇數環走一遍,然後改變所需要的邊數能讓答案更小呢,所以\(k\)
#include<cstdio> #include<cstring> #define N 110 #define NN 1100 #define M 210 using namespace std; template<class T> inline T mymin(T x,T y){return x<y?x:y;} class EDGE { public: struct node { int y,c,next; }a[M];int len,last[NN]; void ins(int x,int y,int c) { len++;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len; } }a1,a2; struct FUCKKK { int x,y,c; }fuck[N]; int f1[M][N],f2[M][N];int n,m,st,ed;bool v[N]; int be[NN],cnt,sta[N]; void dfs(int x) { for(int k=a1.last[x];k;k=a1.a[k].next) { int y=a1.a[k].y; if(!be[y]) { be[y]=++cnt;sta[cnt]=y; dfs(y); } a2.ins(be[x],be[y],a1.a[k].c); } } int main() { scanf("%d%d%d%d",&n,&m,&st,&ed); for(int i=1;i<=m;i++) { int c,x,y;scanf("%d%d%d",&c,&x,&y); a1.ins(x,y,c);a1.ins(y,x,c); fuck[i].x=x;fuck[i].y=y;fuck[i].c=c; } sta[cnt=1]=st;be[st]=1; dfs(st); //處理和st,ed相通的點的個數 memset(f1,30,sizeof(f1)); memset(f2,30,sizeof(f2)); f1[0][1]=f2[0][be[ed]]=0; int limit=a2.len; for(int i=0;i<limit;i++) { for(int j=1;j<=cnt;j++) { for(int k=a2.last[j];k;k=a2.a[k].next) { int y=a2.a[k].y; f1[i+1][y]=mymin(f1[i+1][y],f1[i][j]+a2.a[k].c); f2[i+1][y]=mymin(f2[i+1][y],f2[i][j]+a2.a[k].c); } } } //Bellman - Ford int ans=999999999; for(int i=1;i<=m;i++) { if(!be[fuck[i].x])continue; int x=be[fuck[i].x],y=be[fuck[i].y]; int ed1=mymin(limit,n); for(int j=0;j<=ed1;j++) { int ed2=mymin(limit,n-j); for(int k=0;k<=ed2;k++) { if((n-j-k)&1) { int shit=(int)(n-j-k)*fuck[i].c; ans=mymin(mymin(f1[j][x]+f2[k][y],f1[j][y]+f2[k][x])+shit,ans); } else { int shit=(int)(n-j-k)*fuck[i].c; ans=mymin(mymin(f1[j][x]+f2[k][x],f1[j][y]+f2[k][y])+shit,ans); } } } } printf("%d\n",ans); return 0; }
當然,常數稍稍大了點,但是無所謂啦。