题意:一个点到其他点的距离等于经过的边数*边权和
先选一个点,让其与其它点相连,求最小距离和
说明
当然是爆搜啦~~~~~~~~
#include<cstdio> #include<iostream> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define int long long int n; int m; int f[20][20]; int g[20][20]; int cnt; int tot; int ans=0x7fffffff; inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-f; ch=getchar(); } while(isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } inline void put(int x) { if(x<0) { x=-x; putchar('-'); } if(x>9) put(x/10); putchar(x%10+'0'); } inline void in() { freopen("treasure.in","r",stdin); freopen("treasure.out","w",stdout); } inline void out() { fclose(stdin); fclose(stdout); } inline void Floyd() { for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) g[i][j]=min(g[i][k]+g[k][j],g[i][j]); for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j&&j!=k&&k!=i) f[i][j]=min((f[i][k]+f[k][j])*g[i][j],f[i][j]); // for(int i=1;i<=n;i++) // for(int j=1;j<=n;j++) // cout<<i<<" "<<j<<" "<<f[i][j]<<endl; } signed main() { in(); n=read(); m=read(); memset(f,0x3f,sizeof f); memset(g,0x3f,sizeof g); for(int x,y,z,i=1;i<=m;i++) { x=read(); y=read(); z=read(); if(x==y)continue; f[x][y]=f[y][x]=z; g[x][y]=g[y][x]=1; } Floyd(); for(int i=1;i<=n;i++) { tot=0; for(int j=1;j<=n;j++) { if(j==i) continue; tot+=f[i][j]; } ans=min(ans,tot); } put(ans); out(); return 0; }
其实,在此之前想到了最小生成树,但是发现不对,(贪心目光短浅QAQ)
然后,rqjdalao给了一种niubilitiful的算法:模拟退火!
他可以解决贪心的目光短浅问题
引入随机化,在某些局面时,不选择局部最优解,而选择次优解、次次优解。。。。
让算法执行几百次,取min,成功几率很大!
#include<cstdio> #include<iostream> #include<cstring> #include<cctype> #include<algorithm> #include<ctime> #include<queue> using namespace std; #define int long long int dis[50]; int num[50]; int n; int m; bool vis[50]; int f[50][50]; int ans=0x7fffffff; int ls; struct node { int id; int dis; friend bool operator < (const node &a,const node &b) { return a.dis<b.dis; } }temp[50]; inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-f; ch=getchar(); } while(isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } inline void put(int x) { if(x<0) { x=-x; putchar('-'); } if(x>9) put(x/10); putchar(x%10+'0'); } inline void in() { freopen("treasure.in","r",stdin); freopen("treasure.out","w",stdout); } inline void out() { fclose(stdin); fclose(stdout); } inline int suiji(int i) { double x=(double)i/(double)n; double y=(double)rand()/(double)RAND_MAX; if(y*y>=x) return 2; return 1; } inline int kan_xin_qing(int k) { int tot=0LL; for(int i=1;i<=n;i++) if(!vis[i]&&dis[i]*num[i]<0x7fffffff) temp[++tot]=(node){i,dis[i]*num[i]}; sort(temp+1,temp+tot+1); if(tot<k) return temp[tot].id; return temp[k].id; } inline int Prim(int head) { for(int i=0;i<=n;i++) { vis[i]=0; dis[i]=0x3f3f3f3f; num[i]=0x3f3f3f3f; } int tot=0LL; dis[head]=num[head]=0LL; for(int i=1;i<=n;i++) { int minn=kan_xin_qing(suiji(i)); vis[minn]=true; tot+=dis[minn]*num[minn]; // cout<<tot<<" "; for(int j=1;j<=n;j++) { if(!vis[j]&&dis[j]*num[j]>f[minn][j]*(num[minn]+1)) { dis[j]=f[minn][j]; num[j]=num[minn]+1; } } } return tot; } signed main() { // in(); srand(time(0)); n=read(); m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j]=i==j? 0:0x3f3f3f3f; for(int x,y,z,i=1;i<=m;i++) { x=read(); y=read(); z=read(); if(x==y)continue; f[x][y]=f[y][x]=min(f[x][y],z); } for(int i=1;i<=n*800;i++) { ls=0x7fffffff; for(int j=1;j<=n;j++) ls=min(ls,Prim(j)); // cout<<ls<<" "; ans=min(ans,ls); } put(ans); out(); return 0; }
当然,看到n那么小,自然有了状压DP的做法
二进制每一位代表当前有没有打通到i的路
#include<cstdio> #include<iostream> #include<cstring> #include<cctype> #include<algorithm> #include<ctime> #include<queue> using namespace std; #define int long long int dis[50]; int n; int m; int f[50][50]; int ans=0x7fffffff; int ls; int dp[655360]; inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-f; ch=getchar(); } while(isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } inline void put(int x) { if(x<0) { x=-x; putchar('-'); } if(x>9) put(x/10); putchar(x%10+'0'); } inline void in() { freopen("treasure.in","r",stdin); freopen("treasure.out","w",stdout); } inline void out() { fclose(stdin); fclose(stdout); } inline void dfs(int zt) { for(int i=1;i<=n;i++) //遍历所有点 { if((1<<(i-1))&zt) //如果当前状下i已被遍历 { for(int j=1;j<=n;j++) //遍历与i相邻的点 { if(!((1<<(j-1))&zt)&&f[i][j]<=0x7fffffff) //当前为遍历j并且i与j相连 { if(dp[(1<<(j-1))|zt]>dp[zt]+dis[i]*f[i][j]) //可更新 { int ls=dis[j]; dis[j]=dis[i]+1; //ls用于回溯 dp[(1<<(j-1))|zt]=dp[zt]+dis[i]*f[i][j]; //更新 dfs((1<<(j-1))|zt); //让j进入状态0->1 dis[j]=ls; //回溯 } } } } } } signed main() { // in(); n=read(); m=read(); memset(f,0x5f,sizeof f); for(int x,y,z,i=1;i<=m;i++) { x=read(); y=read(); z=read(); if(x==y)continue; f[x][y]=f[y][x]=min(f[x][y],z); } for(int i=1;i<=n;i++) //枚举每个点为起点 { memset(dis,0x7f,sizeof dis); //每次赋初值 memset(dp,0x7f,sizeof dp); dp[1<<(i-1)]=0; //起点(起点是1,其余为0) dis[i]=1; dfs(1<<(i-1)); //从起点开始搜 ans=min(ans,dp[(1<<n)-1]); //最终状态,全是1 } put(ans); out(); return 0; }