UPC Haywire(模拟退火 || 随机数法)

简介: UPC Haywire(模拟退火 || 随机数法)

中文题面友链 传送门

Haywire

题目描述

Farmer John’s N cows (4 <= N <= 12, N even) have built a primitive system for communicating between pairs of friendly cows by building wires protected by wrappings made of hay.


Each cow has exactly 3 other friends in the barn, and the cows must arrange themselves to occupy N stalls lined up in a row. A wire of length L requires exactly L units of hay to build, so for example if the cows in stalls 4 and 7 are friends, it would take 3 units of hay to construct a wire to connect them.


Assuming every pair of friends must be connected by a separate wire, please determine the minimum possible amount of hay required to build these wires if the cows order themselves in the best possible way.

输入


Line 1: The integer N. FJ’s cows are conveniently numbered 1…N.

Lines 2…1+N: Each line contains three space-separated integers in the range 1…N. Line i+1 contains the numeric identifiers of the three friends of cow i. If cow i is a friend of cow j, then j will also be a friend of i.

输出

Line 1: The minimum total amount of hay required to connect all pairs of friendly cows.

样例输入 Copy

6

6 2 5

1 3 4

4 2 6

5 3 2

4 6 1

1 5 3

样例输出 Copy

17

提示

There are 6 cows. Cow 1 is friends with cows 6, 2, and 5, etc.A best ordering of the cows is 6, 5, 1, 4, 2, 3, which requires only 17 units of hay.

朴素随机数法


数据很小,利用随机数计算每次交换的牛的位置,计算出花费,跑1e6次就可以了。(真玄学) 好像只能是1e6,反正1e5是82,5e5是92,越来越接近正解了

注意计算花费时每两头牛都算了两遍,所以结果需要/2.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define I_int ll
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
char F[200];
inline void out(I_int x) {
    if (x == 0) return (void) (putchar('0'));
    I_int tmp = x > 0 ? x : -x;
    if (x < 0) putchar('-');
    int cnt = 0;
    while (tmp > 0) {
        F[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while (cnt > 0) putchar(F[--cnt]);
    //cout<<" ";
}
const int maxn=20,mod=1e9+7,tot=1e6;
int pos[maxn][maxn],n,a[maxn],tmp[maxn];
void init(){
  for(int i=1;i<=n;i++) a[i]=i;
}
int cul(){
  int sum=0;
  for(int i=1;i<=n;i++) tmp[a[i]]=i;
  for(int i=1;i<=n;i++)
    for(int j=1;j<=3;j++)
      sum+=abs(tmp[pos[a[i]][j]]-i);
  return sum/2;//计算时每两个牛的距离都算了都两遍
}
void AC(){
    n=read();
    for(int i=1;i<=n;i++)
      for(int j=1;j<=3;j++)
        pos[i][j]=read();
    init();
    int cnt=1;
    int minn=2e9;
    while(cnt<=tot){
      int l=(rand())%n+1;
      int r=(rand())%n+1;
      int t=a[l];a[l]=a[r];a[r]=t;
      minn=min(minn,cul());
      cnt++;
  }
  out(minn);
}
int main(){
    AC();
    return 0;
}

稍加优化的模拟退火

其实本质也是随机数,只是多了一个判断,如果当前解很差的话,就再交换回去。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define I_int ll
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
char F[200];
inline void out(I_int x) {
    if (x == 0) return (void) (putchar('0'));
    I_int tmp = x > 0 ? x : -x;
    if (x < 0) putchar('-');
    int cnt = 0;
    while (tmp > 0) {
        F[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while (cnt > 0) putchar(F[--cnt]);
    //cout<<" ";
}
const int maxn=20,mod=1e9+7,tot=5e5;
int pos[maxn][maxn],n,a[maxn],tmp[maxn];
int minn=1e8;
const double s=10000,e=1e-12,c=0.99;
//起始温度 终止温度 温度改变速度 
void init(){
  for(int i=1;i<=n;i++) a[i]=i;
}
int cul(){
  int sum=0;
  for(int i=1;i<=n;i++) tmp[a[i]]=i;
  for(int i=1;i<=n;i++)
    for(int j=1;j<=3;j++)
      sum+=abs(tmp[pos[a[i]][j]]-i);
  return sum/2;//计算时每两个牛的距离都算了都两遍
}
void SA(int times){
  int x,y,t;
  while(times--){
    for(double i=s;i>e;i*=c){
      do{
        x=rand()%n+1;
        y=rand()%n+1;
      }while(x==y);
      swap(a[x],a[y]);
      t=cul();
      if(minn>=t) minn=t;
      else if(exp((minn-t)/i)>(double)rand()/RAND_MAX)
        swap(a[x],a[y]);
        //当前解很差 恢复此次交换
    }
  }
  return ;
}
void AC(){
    n=read();
    for(int i=1;i<=n;i++)
      for(int j=1;j<=3;j++)
        pos[i][j]=read();
    init();
    SA(160);
  out(minn);
}
int main(){
    AC();
    return 0;
}

几率接受的几率是个很玄学的问题,通常情况,设当前温度为 T T ,当前解为 k k ,最优解为 K_{max} K

max

,我们通常用这样一个玄学公式…

exp((k-kmax)/T)<(double)rand()/RAND_MAX

我当初也不知道exp的意义何在,后来发现太妙了!如果不知道exp是啥,可以自己看一下百科,我稍作解释: exp函数在X < 0时, Y 一定为 0 - 1 之间的小数且单调递增,(double)rand()/RAND_MAX也是一个 0 - 1 之间的小数.这样温度越小,接受当前较差解的几率越低.

最后对比一下两种方法的运行结果吧,其实差不了很多~

朴素版本的随机数

60a6bcefe26f4b118e50f46e4d0afd1d.png稍加优化的模拟退火

60a6bcefe26f4b118e50f46e4d0afd1d.png

还有个正解是状压dp 想想数据这么小确实可以状压做 未完待续


参考资料:

关于模拟退火的学习笔记 - 李有才99NL的博客 - 洛谷博客

题解 P2210 【Haywire】 - Ciyang 的博客 - 洛谷博客

Haywire (随机数大法好)_weixin_43916298的博客-CSDN博客

目录
相关文章
|
15小时前
|
API
PTA-给定精度,求圆周率PI的近似值
给定精度,求圆周率PI的近似值
45 1
|
15小时前
|
C++
【PTA】​L1-048 矩阵A乘以B​ (C++)
【PTA】​L1-048 矩阵A乘以B​ (C++)
36 0
【PTA】​L1-048 矩阵A乘以B​ (C++)
|
9月前
|
机器学习/深度学习 算法
Lecture 6:值函数近似
Lecture 6:值函数近似
|
12月前
|
存储 缓存 C++
C++/PTA 对称排序
你供职于由一群丑星作为台柱子的信天翁马戏团。你刚完成了一个程序编写,它按明星们姓名字符串的长度非降序(即当前姓名的长度至少与前一个姓名长度一样)顺序输出他们的名单。然而,你的老板不喜欢这种输出格式
49 0
PTA 1006 Sign In and Sign Out (25 分)
At the beginning of every day, the first person who signs in the computer room will unlock the door, and the last one who signs out will lock the door.
89 0
|
人工智能
Codeforces1343D - Constant Palindrome Sum + UPC-鸭子游戏 (差分)
Codeforces1343D - Constant Palindrome Sum + UPC-鸭子游戏 (差分)
76 1
UPC-趾压板矩阵(强行找规律)
UPC-趾压板矩阵(强行找规律)
76 0
UPC-趾压板矩阵(强行找规律)
|
人工智能
【待补】UPC No Need(二分+bitset || 背包dp)
【待补】UPC No Need(二分+bitset || 背包dp)
41 0
|
人工智能
UPC-魔法序列(结论+枚举)
UPC-魔法序列(结论+枚举)
61 0
UPC-魔法序列(结论+枚举)
[USACO | UPC] Liars and Truth Tellers | 拓展域并查集
题目描述 After spending so much time around his cows, Farmer John has started to understand their language. Moreover, he notices that among his N cows (2 ≤ N ≤ 1000 ), some always tell the truth while others always lie.
108 0