首页 > 其他分享 >随机化贪心

随机化贪心

时间:2023-11-05 18:00:42浏览次数:39  
标签:maxx int rint 随机化 随机 贪心

随机化

什么是随机化算法?

随机化做法,就是基于当前算法而言,通过正确算法求解会 TLE 或者 MLE,但是出于骗分的目的,我对可能答案中的一些序列或者值进行查找,在我随机瞎找的这些值中去找正确答案,最后,所得出的答案具有极大可能性为正确答案,甚至于百分之一百。

如果题目要求最优解,但难以按照某个规则贪心求出最优解,也无法使用动态规划等算法。可以考虑随机贪心,将输入数据随机打乱,然后从前到后按照某种方式贪心,多次随机求最优值。

还可以结合多种不同的贪心规则,每次使用不同的贪心方法,不断逼近最优值。

一般可以用在出题人无法轻易掌控某个输入时得到的输出类型的题目,随机情况下可以避免出题人的刻意卡。同时,随机化贪心也可以加入权重,例如为直观感受上更优的点提供更高的权重,更有几率排在序列的前面。

如何为更优的点提供更高的权值呢?如果某个点存在多个有关量,首先我们把有关量分类,类间的关联性较小,类间的权值用加法。同类的量用比值定义法,越重要的量就在上面加次方就可以了。

随机化解决例题

[POI2004] PRZ

题目传送门

正解是状压 dp

但我想玩随机化。

\(id\) 表示贪心的顺序,初始的时候让 \(id_i=i\),之后每次都对 \(id\) 进行 random_shuffle,按这个新的顺序贪心,每次记录所得到的最小值,最后最小的那个就是答案。

signed main()
{
    srand(time(0));
    cin >> v >> n;
    
    for (rint i = 1; i <= n; i++)
    {
		cin >> t[i] >> w[i];
		id[i] = i;
    }
        
    int times = 3e5 + 5;
    while (times--)
    {
        random_shuffle(id + 1, id + n + 1);
        int sum = 0, cnt = 0;
		int maxx = 0;
	    for (rint i = 1; i <= n; i++)
	    {
	        int x = id[i];
			if (sum + w[x] > v)
	        {
	            cnt += maxx;
	            sum = w[x];
	            maxx = t[x];
	        }
	        else
	        {
	            sum += w[x];
	            maxx = max(maxx, t[x]);
	        }
	    }
	    minn = min(minn, cnt + maxx);
    }
    
    cout << minn << endl;
    
	return 0;
}

[TJOI2015] 线性代数

题目传送门

正解是矩阵乘法 + 网络流

但我想玩随机化

每次随机一个位置,把这个位置取反,判断大小并更新答案。然后官方数据全能水过。luogu 上三个 hack 数据有一个过不了

void calc() 
{
    memset(d, 0, sizeof d);
    int now = 0;
    for (rint i = 1; i <= n; i++)
        for (rint j = 1; j <= n; j++)
            d[i] += a[j] * c[i][j];		
            
    for (rint i = 1; i <= n; i++)
        now += (d[i] - b[i]) * a[i];
    ans = max(ans, now);
}
signed main() 
{
    cin >> n;
    for (rint i = 1; i <= n; i++)
		for (rint j = 1; j <= n; j++)
			cin >> c[i][j];
    for (rint i = 1; i <= n; i++)
    {
		cin >> b[i];a[i] = 1;
	}
    calc();
    int times = 1e3 + 5;
    while(times--)
	{
        int x = rand() % n + 1;
        a[x] ^= 1;
        calc();
    }
    cout << ans << endl;
    return 0;
}

[春季测试 2023] 密码锁

题目传送门

这个题我不会正解,连题解都看不懂。

但是我想玩随机化。

考虑贪心,有一个假的贪心为:从 \(1 \sim n\) 考虑,对于每个转圈枚举出转多少对总答案的影响最小。

虽然这个贪心是错的,但是我如果多随机几次,从随出来的这些数据找答案呢???

然后恭喜你,这道题你可以切了,一般来说交个七八次就过了。

signed main()
{
	srand(time(0));
	T = read();
    k = read();
    while (T--)
    {
        n = read();
        for (rint i = 1; i <= k; i++)
            for (rint j = 1; j <= n; j++)
                a[j][i] = read();;
        int times = 150;
        while (times--)
        {
            random_shuffle(a + 1, a + n + 1);
            memset(maxx, -0x3f, sizeof maxx);
            memset(minn, 0x3f, sizeof minn);
            for (rint i = 1; i <= n; i++)
            {
                int v = inf;
                int p = 0;
                for (rint x = 0; x ^ k; x++)
                {
                    int w = 0;
                    for (rint j = 0; j <= k; j++)
                    {
                        int o = (j + x - 1) % k + 1;
                        w = max(w, max(maxx[j], a[i][o]) - min(minn[j], a[i][o]));
                    }
                    v = w < v ? (p = x, w) : v;
                }
                for (rint j = 1; j <= k; j++)
                {
                    int o = (j + p - 1) % k + 1;
                    maxx[j] = max(maxx[j], a[i][o]);
                    minn[j] = min(minn[j], a[i][o]);
                }
                if (ans <= v)
                {
                    break;
                }
            }
            int res = 0;
            for (rint i = 1; i <= k; i++)
            {
                int delta = maxx[i] - minn[i];
                res = max(res, delta);
            }
            ans = min(ans, res);
        }
        cout << ans << endl;
    }
    return 0;
}

CF329C

题目传送门

这道题的正解好像就是随机化???

但是我想玩随机化

不断地随机生成一个链的形式,判断它是否在原图中出现过,如果没有,直接输出。然后如果随机了好几次都找不到,则输出 -1

signed main()
{
	cin >> n >> m;
	for (rint i = 1; i <= m; i++)
	{
		int a, b;
		cin >> a >> b;
		d[a][b] = d[b][a] = 1;
	}
	for (rint i = 1; i <= n; i++) a[i] = i;
	int times = 1e3 + 5;
	while(times--)
	{
		random_shuffle(a + 1, a + n + 1);
		if(!check())
		{
			int len = min(n, m + 1);
			for (rint i = 2; i <= len; i++)
			    cout << a[i - 1] << " " << a[i] << endl; 	
            if (n == m)
				cout << a[n] << " " << a[1];
			return 0;
		}
	}
    cout << -1 << endl;
    return 0;
}

CF798D

题目传送门

这个题的正解是一个非常复杂的分组贪心。

但是我想玩随机化

每次随机打乱原数列

然后按这个新的数列贪心地取前$ \frac{n}{2}+1$ 个数,一直到合法为止

bool check()
{
    int a = 0, b = 0;
    for (rint i = 1; i <= n / 2 + 1; i++)
        a += f[i].a, b += f[i].b;
    return a * 2 > s1 && b * 2 > s2;
}
signed main()
{
    cin >> n;
    for (rint i = 1; i <= n; i++)
    {
		cin >> f[i].a;
		s1 += f[i].a;
		f[i].id = i;
	}
    for (rint i = 1; i <= n; i++)
	{
        cin >> f[i].b;
        s2 += f[i].b;
    }
    cout << n / 2 + 1 << endl;
	while(1)
	{
        random_shuffle(f + 1, f + n + 1);
        if(check())
		{
            for (rint i = 1; i <= n / 2 + 1; i++)
            {
				cout << f[i].id << " ";
			}
            return 0;
       }
    }
    return 0;
}

[WC2018] 通道

题目传送门

这道题的正解是虚树分治

但是我想用随机化

对于一个节点跑 SPFA,查找在三棵树上和自己距离和最大的节点。

所以我们可以:不断寻找与自己在三棵树上总距离最大的节点,并跳到那个节点上。

然后随机选节点,走十次,找最大值记录答案。

随个五遍就过了

struct tree
{
    int h[N], ne[N], e[N], w[N], idx = 0;
    int dist[N];
    bool v[N];
    void add(int a, int b, int c) 
	{
        ne[++idx] = h[a], e[idx] = b, w[idx] = c, h[a] = idx;
    }
    void SPFA(int s) 
	{
        queue<int> q;
        memset(v, 0, sizeof v);
        memset(dist, 0x7f, sizeof dist);
        q.push(s);
		v[s] = 1;
        dist[s] = 0;
        while (!q.empty()) 
		{
            int x = q.front();
            q.pop();
            for(rint i = h[x]; i; i = ne[i])
			{
                int y = e[i];
                int z = w[i];
                if (v[y])
                {
					continue;
				}
                dist[y] = dist[x] + z;
                q.push(y);
                v[y] = 1;
            }
        }
    }
} t[4];
signed main() 
{
    srand(time(0));
    cin >> n;
    int times = 3.7 * CLOCKS_PER_SEC;
    for (rint k = 1; k <= 3; k++)
	{
        for (rint i = 1; i < n; i++)
		{
            int a, b, c;
            cin >> a >> b >> c;
            t[k].add(a, b, c);
			t[k].add(b, a, c);
        }
    }
    for (rint i = 1; i <= n; i++) 
	{
		p[i] = i;
	}
    random_shuffle(p + 1, p + n + 1);
    int idx = 1, ans = 0;
    while (idx <= n && clock() <= times) 
	{
        int u = p[idx];
        for (rint k = 1; k <= 10; k++) 
		{
            t[1].SPFA(u);
			t[2].SPFA(u);
			t[3].SPFA(u);
            int maxx = 0;
            for (rint i = 1; i <= n; i++)
			{
                int res = 0;
                for (rint j = 1; j <= 3; j++) 
				{
					res += t[j].dist[i];
				}
                if (res > maxx)
                {
                    maxx = res;
					u = i;					
				}
            }
            ans = max(ans, maxx);
            //不断随机数列去找最大值
        }
        idx++;
    }
    cout << ans << endl;
    return 0;
}

[NOIP2021] 方差

题目传送门

众人都说模拟退火过不了,但是吧,我用 mt19937 卡过去了。。。。

int calc() 
{
    int s = 0, w = 0;
    int p = 0;
    for (rint i = v1.size() - 1; i >= 0; i--) 
	{
        p += v1[i];
        s += p;
		w += p * p;
    }
    for (auto i : v2)
    {
        p += i;
        s += p;
		w += p * p;
    }
    w = w * n - s * s;
    ans = min(ans, w);
    return w;
}
mt19937 neww(time(NULL));
double Rand() 
{
    return (double)(neww() % inf) / inf;
}
void solve() 
{
    double t = 5e5;
    long long now = ans;
    v1 = v3, v2 = v4;
    int x, val;
    while (t > eps) 
	{
        if ((clock() - tim) * 1000 >= 980 * CLOCKS_PER_SEC) 
		{
            cout << ans << endl;
            exit(0);
        }
        x = neww() % (n - 1);
        v5 = v1;
		v6 = v2;
        if (x < (int)v1.size()) 
		{
            val = v1[x];
            v1.erase(v1.begin() + x);
            v2.insert(lower_bound(v2.begin(), v2.end(), val), val);
        } 
		else 
		{
            x -= v1.size();
            val = v2[x];
            v2.erase(v2.begin() + x);
            v1.insert(lower_bound(v1.begin(), v1.end(), val), val);
        }
        int delta = calc() - now;
        if (delta < 0 || exp(-delta / t) > Rand())
        {
            now += delta;			
		}
        else
        {
            v1 = v5;
			v2 = v6;			
		}
        t *= 0.999;
    }
}
signed main() 
{
    tim = clock();
    cin >> n;
    for (rint i = 1; i <= n; i++)
    {
		cin >> a[i];
		c[i] = a[i] - a[i - 1];
	}
    sort(c + 2, c + n + 1);
    for (rint i = 2; i <= n; i += 2)
    {
        v1.push_back(c[i]);		
	}
    for (rint i = 3; i <= n; i += 2)
    {
        v2.push_back(c[i]);		
	}
    v3 = v1;
	v4 = v2;
    calc();
    while (1)
    {
		solve();
	}
    return 0;
}

推荐例题

以下题目均可使用随机化润过

标签:maxx,int,rint,随机化,随机,贪心
From: https://www.cnblogs.com/spaceswalker/p/17810559.html

相关文章

  • 贪心算法(C语言)
    一、会议安排问题1.1问题(1)对于每个会议i,起始时间bi和结束时间ei,且bi<ei(2)[bi,ei]与[bj,ej]不相交,则会议i和会议j相容,bi≥ej或bj≥ei(3)目标:在有限的时间内,尽可能多地安排会议1.2分析选择最早结束的会议1.3实现(1)初始化:按结束时间递增排序(2)选中第一......
  • 贪心算法之找零钱
    defgreedy_change(amount,coins):coins.sort(reverse=True)#将硬币按面额从大到小排序change=[]forcoinincoins:whileamount>=coin:amount-=coinchange.append(coin)#将硬币加入到找零列表中returnch......
  • 贪心法
      ......
  • C++U4-02-贪心算法2
    上节课作业部分  [纪念品分组]  【算法分析】贪心算法:先对数组从小到大排序,用l=1,r=n指针指向首尾元素;如果pl+pr≤w,则将pl和pr分一组,指针l++,r--。如果pl+pr>w,则将pr单独作为一组,指针r--。如此反复直到取完所有元素。#include<iostream>#include<a......
  • 力扣1444.切割后面积最大的蛋糕(贪心)
    矩形蛋糕的高度为 h 且宽度为 w,给你两个整数数组 horizontalCuts 和 verticalCuts,其中: horizontalCuts[i] 是从矩形蛋糕顶部到第  i 个水平切口的距离verticalCuts[j] 是从矩形蛋糕的左侧到第 j 个竖直切口的距离请你按数组 horizontalCuts 和 verticalCuts......
  • SQLSmith: Databend 如何利用随机化测试检测 Bug
    作者:白珅Databend 研发工程师https://github.com/b41sh为什么需要SQLSmith?在数据库系统的开发和维护过程中,测试扮演着至关重要的角色。它不仅可以验证功能的正确性,还可以发现潜在的问题,确保数据库在每个变更和迭代后保持性能和稳定性。Databend的CI已经支持了多种类......
  • 贪心法求解问题
    一、背包问题1.1问题描述  设有编号为1、2、......、n的n个物品,它们的重量分别为w1、w2、......、wn,价值分别为v1、v2、......、vn,其中wi和vi均为正数,有一个背包可以懈怠的最大重量不超过W。求解目标是在不超过背包附中的前提下使背包装入的总价值最大(即效益最大化)。与0/1背......
  • 贪心算法
    顾名思义,贪心,即永远选择当下情况下最佳的结果,也就是所谓的局部最优解。该算法寄希望于局部最优解的堆积可以形成总体上的最优算法。注意:可以使用反证法来判断贪心算法是否可以计算出最优路径。注:大部分有限选择的情况都可以通过有限状态机解决。......
  • 【基础算法】- 贪心
    贪心定义贪心算法适用于最优子结构问题。意思是问题在分解成子问题来解决时,子问题的最优解能递推到最终问题的最优解。常见的符合这种性质的问题如:「我们将XXX按照某某顺序排序,然后按某种顺序(例如从小到大)选择。」「我们每次都取XXX中最大/小的东西,并更新XXX。」但比......
  • Codeforces Round 905 (Div. 2) D1. Dances (Easy version)(贪心+二分)
    CodeforcesRound905(Div.2)D1.Dances(Easyversion)思路:对于\(a\),它的头默认为\(1\),则\(a_0\)=\(1\)对于排完序的\(a\)与\(b\)数组最优为从\(a\)的结尾删除,从\(b\)的开头删除二分保留位数,删去\(n-mid\)位,即\(a\)从\(0\)开始,\(b\)从\(k\)(\(k=n-......