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博客

目录
相关文章
|
算法 测试技术 C++
剑指offer(C++)-JZ20:表示数值的字符串(算法-模拟)
剑指offer(C++)-JZ20:表示数值的字符串(算法-模拟)
|
算法 C++
剑指offer(C++)-JZ67:把字符串转换成整数atoi(算法-模拟)
剑指offer(C++)-JZ67:把字符串转换成整数atoi(算法-模拟)
|
机器学习/深度学习
CF1304B Longest Palindrome(可逆转符号,数学分析,回文串)
CF1304B Longest Palindrome(可逆转符号,数学分析,回文串)
50 0
|
人工智能
Codeforces1343D - Constant Palindrome Sum + UPC-鸭子游戏 (差分)
Codeforces1343D - Constant Palindrome Sum + UPC-鸭子游戏 (差分)
107 1
【每日一题Day104】LC2319判断矩阵是否是一个 X 矩阵 | 模拟
思路:如果对角线的元素等于0或者其他元素不等于0,那么返回false
86 0
|
人工智能
【待补】UPC No Need(二分+bitset || 背包dp)
【待补】UPC No Need(二分+bitset || 背包dp)
60 0
|
人工智能 开发者
魔法序列-upc
题目描述 小E为了完成公主的任务,需排布魔法阵,从中获得法力。 简单起见,魔法阵可以看成一个长度为n的序列。序列从左到右都摆放了一张符卡,符卡有一个强度ai。法术的释放要每个元素相互配合,取得共鸣效果。一个由一些符卡组成的咒语的魔力值为这个咒语中所有符卡的强度的最大公因数乘以符卡的个数。 小E会从魔法阵中选择一段连续符卡区间[l,r](包括l,r端点),作为吟唱的咒语。她想知道,咒语最大的魔力值是多少。
172 0
魔法序列-upc
|
Java C++
HDU-1005,Number Sequence(有规律的数学题)
HDU-1005,Number Sequence(有规律的数学题)
UPC山头狙击战--二分
题目描述 Lucky为了掩护大部队,单枪匹马同敌人周旋,后来被敌人包围在某山头……等等,为什么怎么听怎么像狼牙山五壮士!不过不用着急,这次Lucky携带了足够的弹药,完全可以将涌上来的敌人一个一个干掉。Lucky是个神枪手,只要他的枪膛中有子弹,他就能将在他射程m(用从敌人位置到山头的直线距离算)以内的一个敌人瞬间射杀。但如果在射程内没有敌人,出于节约子弹考虑和面子问题,Lucky会等待敌人靠近然后射击。
126 0
|
算法 C# 图形学
C# 之 随机数应用 -- 洗牌算法
随机数实例洗牌算法,超级简单的真随机算法。
386 0
C# 之 随机数应用 -- 洗牌算法