入门班课枚举贪心习题

专题
明明的随机数
数据很小 ,适合桶排序

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const ll N = 1e6 + 9;
int n, m, k, t;
int a[N], b[N];
void work()
{
	cin >> n;
	for(int i = 1; i <= n; ++i) 
	{
		cin >> k;
		if(a[k]) continue;
		a[k] = 1,t++;
	}
	cout << t << endl;
	for(int i = 1; i <= 1000; ++i)
	{
		if(a[i]) cout << i <<" ";
	}
	
}
int main()
{
	//cin >> t;
	//while(t--)
		work();
	return 0;
}

糖糖别胡说,我真的不是签到题目
这个题解的思路是
先考虑m次操作 把 能力值都变成最终态
然后从后向前遍历 维护两个max
只要小于max一定会被消灭

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 500005;
struct node
{
    int x;
    ll y;
} a[maxn];
int b[maxn];
int main()
{
    int t,n,m,x;
    scanf("%d",&t);
    while(t--)
    {
        
        scanf("%d%d",&n,&m);
        memset(b,0,(n+1*4));
        for(int i=1; i<=n; i++)scanf("%d%lld",&a[i].x,&a[i].y);
        for(int i=1; i<=m; i++)scanf("%d",&x),b[x]++;
        for(int i=n; i>=1; i--)b[i] += b[i+1], a[i].y += b[i];
        int cnt = 0;
        ll max0 = 0, max1 = 0;
        for(int i=n; i>=1; i--)
        {
            if(a[i].x == 0)
            {
                max0 = max(max0, a[i].y);
                if(max1 > a[i].y)
                    cnt++;
            }
            else
            {
                max1 = max(max1, a[i].y);
                if(max0 > a[i].y)
                    cnt++;
            }
        }
        printf("%d\n",n - cnt);
    }
    return 0;
}
/*
1
4 3
0 3
1 2
0 3
1 1
1
3
4
*/

奇♂妙拆分
题意:一个自然数最多可以分解成多少个不同的因子的乘积
枚举题,主要在暴力的基础上减去不必要的枚举。
1.因为n(1除外)一定存在一个小于根号n的因子,所以枚举到因子枚举到根号n就可以了。
2.每找到一个因子就从n分离出来:n /= i;
3.当时i*i == n,有两个一样的未出现的因子,那么只能把这两个因子i*i合并为算作一个因子。
当时i*i > n,(假设x是上一个分解的因子)表示没有比x还大的因子对了,就是比如 n = a*b,a <= b,需要a和b都大于x,不然分解出b后分解a时一定会重,因为a小于分解出来的一些因子,说明a的因子包括a,前面已经出现过了。
所以如果i*i >= n,把他算作一个因子,不能再分解了。

#include<bits/stdc++.h>
#define  js  ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
int main() {
    js; int t,n;
    cin>>t;
    while(t--) {
        int ans=0; cin>>n;
        for(int i=1;i*i<n;++i) {
            if(n%i==0) {
                ++ans;
                n/=i;
            }
        }
        cout<<ans+1<<endl;
    }
}

另:
一直找n能整除的最小数,模拟即可
代码如下:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    long long n;
    int T;
    cin>>T;
    while(T--){
        cin>>n;
        int cnt=0,i=1;
        while(n&&i<=n){
            if(n%i==0)n/=i,cnt++;
            i++;
        }
        cout<<cnt<<endl;
    }
}

回文日期
题目要求统计回文日期,相当于回文字符串,可以考虑使用遍历
如果固定年份,对月和日进行遍历再判断是否回文效率太低,可以直接遍历月和日,根据月和日构造回文的日期,这样最多只有366种可能。
列出所有的回文日期以后再判断是否再输入的区间之内即可

#include<iostream>

using namespace std;

int main() {
    short days[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int begin, end;
    cin >> begin >> end;
    int count = 0;
    for (int month = 1; month <= 12; ++month) {
        for (int day = 1; day <= days[month]; ++day) {
            int year = (day % 10) * 1000 + (day / 10) * 100 + (month % 10) * 10 + (month / 10);
            int date = year * 10000 + month * 100 + day;
            if (date >= begin && date <= end)count++;
        }
    }
    cout << count<<endl;
}

拼数
题解思路很巧,转化成字符串使问题变得很浅显易懂

先把整数化成字符串,然后再比较a+b和b+a,如果a+b>b+a,就把a排在b的前面,反之则把a排在b的后面,最后输出排序后的字符串,即可得到最大的整数(如果求最小的整数,则从小到大排序)。

举例说明:a=‘123’,b=‘71’,a+b=‘12371’,b+a=‘71123’,所以a+b<b+a,将b排在前面

注意:正常的字符串存在比较缺陷,如:A=’321’,B=’32’,按照标准的字符串比较规则因为A>B,所以A+B > B+A ,而实际上’32132’ < ’32321’。

具体步骤如下:
1.获取n
2.依次获取n个正整数,将整数转换为字符串:声明字符串数组a[n],将获取到的正整数存入数组a中,即可实现正整数到字符串的转换
3.自定义排序函数:如果a+b>b+a,则把a排在前面,否则将b排在前面(对于字符串a、b,a+b表示连接两个字符串形成一个新串)
4.从大到小输出排序后的字符串即可得到最大的整数

#include<bits/stdc++.h>
using namespace std;
bool cmp(string a,string b)
{
    return a+b>b+a;//如果a+b>b+a,则把a排在前面,否则将b排在前面
}
int main()
{
    int n;
    cin>>n;
    string s[n];
    //获取n个正整数,存入字符串数组a中
    for (int i = 0; i < n; ++i)
    {
        cin>>s[i];
    }

    sort(s,s+n,cmp);
    for (int i = 0; i < n; ++i)
    {
        cout<<s[i];
    }
    return 0;
}

巨石滚滚
思路一
这是道究极无敌贪心,需要贪的地方太多了,直接wa了五六次,其实我的代码也是很笨拙的,但是代码的思路还是挺清晰,可以一看的
//核心思路:当然是一贪到底;
给出的数据是无序的,所以我们要想出最优的排法尽量的让巨石把所有的障碍全部撞破,所以我们就要贪他;
所有的数据我们可以分为三种类型,分别是阻碍>恢复(撞到这种障碍之后自身稳定性必减),阻碍==恢复的(稳定性不变但是要考虑撞得破不),阻碍<恢复(撞这种之后自身稳定性增加),所以我们首先要撞的是第三种让自己的稳定性增加才能去撞击后面稳定性减少或者不变的阻碍,但是呢这还不够贪,在全部都是增加的阻碍中我们又按什么来撞呢??(当然是按阻碍性小的先撞,然后稳定性增加之后才能去撞后面阻碍性大的,
再之后其实稳定性不变的就可随便什么顺序,再这之后的稳定性减弱的数据中我们又该怎么撞呢?为了尽量多撞所以我们要将恢复力多的排在前面先撞,这样才能有足够多的稳定性去面对之后的阻碍。
思路就是这样,然后就看看我用代码实现吧(我的代码很蠢,可以看看思路就行了)
思路二
贪心的排序:
1.将表现为恢复的障碍物放前头,表现为扣血的障碍物放后头,是为了能有更好的状态面对障碍物。
2.前头的具体排序:由于每一个障碍物都表现为恢复,同样是为了能有更好的状态面对障碍物,所以a大的放后头。
3.后头的具体排序:
思路历程:
①:一直在扣血,那么将a大的放前面尽早解决,先啃硬骨头。(忽略了b带来的影响)
否定:啃完恢复的少了,那没那么硬的骨头你也啃不掉了。
7 1
5 3
如果有10滴血,打完后者能打前者,打完前者打不了后者了。

②:保留最好的状态去打下一个人,即把扣的血a-b少的放前面。
否定:关键不仅在于有更好的状态,而且还要能打赢下一个a。(忽略了a带来的影响)
1 0扣的血:-1.
7 5扣的血:-2.
如果有7滴血,打完前者打不了后者了,打完后者还能打前者。

将思路放到能不能打赢的边界,即AB能打赢,BA就打不赢了。
那么设立a1 b1,a2 b2,如果AB能打赢,BA就打不赢了,
那么会满足:AB受到的伤害<BA受到的伤害,即公式:a1-b1+a2<a2-b2+a1
化简为:b1>b2
将b大的放前头,才能不死,这就是当下最优解延伸出的全局最优解。

关键是写好排序代码

#include<bits/stdc++.h>
using namespace std;
typedef  long long ll ;

const ll N = 1e6;
struct ran
{
    int a, b, c;
}tr[N];

bool cmp(ran x, ran y){
    if(x.c > 0 && y.c > 0){//贪ai小的  俩正,把ai小的排前边 
        return x.a < y.a;
    }
    else if(x.c < 0 && y.c < 0)//贪bi大的  俩负, 把bi大的排前边 
        return x.b > y.b;
    else   //贪回复大的  一正一负 || 一0一负 || 一0一正   肯定把c大的排前边 
        return x.c > y.c;
}

int main()
{
    ll t, n, m;
    cin>>t;
    while (t--) {
        int k = 1;
        cin>>n>>m;
        for(int i = 1; i <= n; ++i){
            cin>>tr[i].a>>tr[i].b;
            tr[i].c = tr[i].b - tr[i].a;
        }
        sort(tr + 1, tr + n + 1, cmp);
        for(int i = 1; i <= n; ++i){
            m -= tr[i].a;
            if(m < 0)
            {
                k = 0;
                break;
            }
            m += tr[i].b;
        }
        if(k == 0)
            cout<<"No\n";
        else
            cout<<"Yes\n";
    }   
    return 0;
}

激光炸弹
就是简单的前缀和的运用
求 边长为 R 的正方形的最大和

#include<bits/stdc++.h>
using namespace std;
const int N = 5e3 + 100;
int a[N][N];
int main()
{
	int n, r;
	cin >> n >> r;
	int x, y, v;
	for(int i = 1; i <= n; ++i)
	{
		cin >> x >> y >> v;
		a[x+1][y+1] = v;
	}
	for(int i = 1; i <= N-9; ++i)
	{
		for(int j = 1; j <= N-9; ++j)
		{
			a[i][j] = a[i][j] + a[i-1][j] + a[i][j-1] - a[i-1][j-1];
		}
	}
	int max1 = 0;
	for(int i = r; i <= N-9; ++i)
	{
		for(int j = r; j <= N-9; ++j)
		{
			max1 = max(max1,a[i][j] - a[i-r][j] - a[i][j-r] + a[i-r][j-r]);
		}
	}
	cout << max1 << endl;
	return 0;
}

秘法地震
也是一道用前缀和做起来很简单的题
但是需要想到前缀和

#include<bits/stdc++.h>
using namespace std;
const int N = 2e3 + 100;
int xa[N][N];
char a[N][N];
int main()
{
	int n ,m , k;
	cin >> n >> m >> k;
	for(int i = 1;i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			cin >> a[i][j];
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
		{
			xa[i][j] = a[i][j] - '0';
		}
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			xa[i][j] = xa[i][j] + xa[i-1][j]+ xa[i][j-1] - xa[i-1][j-1];
			
	int ans = 0;
	for(int i = k; i <= n; ++i)
		for(int j = k; j <= m; ++j)
		{
			if(xa[i][j] - xa[i-k][j] - xa[i][j-k] + xa[i-k][j-k] >= 1) ans++;
		}
		cout << ans << endl;
	return 0;
}

毒瘤xor
在这个题之前有个简单点的xor cfA题
与这个题的分析有点类似 第一个题

考的核心是位运算 异或的性质
思路:
用前缀和预处理31位二进制数每一位1的个数,区间[L, R]上每一位如果1的个数多于0的个数,X对应二进制位上值为0,否则为1
(意思就是尽量让 x 和该区间的数的每一位异或得到更多的1,这样加起来的数就更大)
因为区间查找,这时我们就要想到对前缀和的利用,用两个端点的计算得出这个区间的结果;开一个二位数组来将一个数竖着储存
注意:若有多组可行解,需要输出较小的解,则当0和1个数一样时,X对应二进制位上取0

#include <bits/stdc++.h>

using namespace std;
const int N = 1000000;
int pp[N],a[35][N],n;
// a[j][i] 存的是 前i个数的第j位的1的个数
int main()
{
    long long int cnt;
    //cnt 有点大,其实int就够了都是防止万一爆空间还是用的long long;
    int m;
    cin>>n;
    for(int i=1; i<=n; i++)
    {
        cin>>pp[i];
        for(int j=0; j<=30; j++)
        {
            a[j][i]=a[j][i-1]+((pp[i]>>j)&1);
            //pp[i]右移j位后与1做按位且运算,即pp[i]的二进制串最后一位是1结果为1否则为0;

        }
    }
    cin >> m;
    for(int i = 1,x,y; i <= m; ++i)
    {

        cin >> x >> y ;
        cnt = 0;
        for(int j = 0; j < 31; ++j)
        {
            int d = a[j][y] - a[j][x-1];//因为边界是要算进去的所以y--x之间为y-(x-1);
            int len = y-(x-1);
            if(d < len-d)// 1的个数小于0的个数 x的第j位就取1
            {
                cnt += 1 << j;//1<<j相当于pow(2,j)即将二进制转换为十进制;
            }
        }
        cout << cnt << endl;
    }
    return 0;
}

基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究(Matlab代码实现)内容概要:本文围绕“基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究”,介绍了利用Matlab代码实现配电网可靠性的仿真分析方法。重点采用序贯蒙特卡洛模拟法对配电网进行长时间段的状态抽样与统计,通过模拟系统元件的故障与修复过程,评估配电网的关键可靠性指标,如系统停电频率、停电持续时间、负荷点可靠性等。该方法能够有效处理复杂网络结构与设备时序特性,提升评估精度,适用于含分布式电源、电动汽车等新型负荷接入的现代配电网。文中提供了完整的Matlab实现代码与案例分析,便于复现和扩展应用。; 适合人群:具备电力系统基础知识和Matlab编程能力的高校研究生、科研人员及电力行业技术人员,尤其适合从事配电网规划、运行与可靠性分析相关工作的人员; 使用场景及目标:①掌握序贯蒙特卡洛模拟法在电力系统可靠性评估中的基本原理与实现流程;②学习如何通过Matlab构建配电网仿真模型并进行状态转移模拟;③应用于含新能源接入的复杂配电网可靠性定量评估与优化设计; 阅读建议:建议结合文中提供的Matlab代码逐段调试运行,理解状态抽样、故障判断、修复逻辑及指标统计的具体实现方式,同时可扩展至不同网络结构或加入更多不确定性因素进行深化研究。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值