0x03 基本算法-递归

1.A [HNOI2003]激光炸弹

链接:https://ac.nowcoder.com/acm/contest/999/A
来源:牛客网

一种新型的激光炸弹,可以摧毁一个边长为R的正方形内的所有的目标。
现在地图上有n(N ≤ 10000)个目标,用整数Xi,Yi(其值在[0,5000])表示目标在地图上的位置,每个目标都有一个价值。
激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为R的正方形的边必须和x,y轴平行。
若目标位于爆破正方形的边上,该目标将不会被摧毁。

输入描述:

输入文件的第一行为正整数n和正整数R,接下来的n行每行有3个正整数,分别表示 xi,yi ,vi 。

输出描述:

输出文件仅有一个正整数,表示一颗炸弹最多能炸掉地图上总价值为多少的目标(结果不会超过32767)。

示例1

输入

2 1
0 0 1
1 1 1

输出

1

备注:

对于100%的数据,保证 1≤n≤10^4,0≤xi,yi≤5×10^3,1≤m≤5×10^3,1≤vi<100。

思路: 二维差分

#include<stdio.h>
#include<iostream>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
const int maxn=5010;
int maps[maxn][maxn];
int val[maxn][maxn];//计算前缀和
int main()
{
    int T;
    int R;//炸弹边界
    cin>>T;
    cin>>R;
    int xx=R,yy=R;//确认地图边界
    int x,y,w;
    while (T--){
        cin>>x>>y>>w;
        x++;
        y++;//将初始坐标改为(1,1)开始
        maps[x][y]=w;
        xx=max(x,xx);
        yy=max(y,yy);//初始化边界
    }
    for(int i=1;i<=xx;i++)//计算前缀和
    for(int j=1;j<=yy;j++){
        val[i][j]=val[i-1][j]+val[i][j-1]-val[i-1][j-1]+maps[i][j];
    }
    int ans=0;//计算炸弹所能花费的最大价值
    for(int i=R;i<=xx;i++)
    for(int j=R;j<=yy;j++){
        ans=max(ans,val[i][j]-val[i-R][j]-val[i][j-R]+val[i-R][j-R]);
    }
    cout<<ans;
    return 0;
}
2.B IncDec Sequence

链接:https://ac.nowcoder.com/acm/contest/999/B
来源:牛客网

题目描述

给定一个长度为 n(n≤10^5) 的数列a1,a2,…,an;
每次可以选择一个区间 [l,r],使下标在这个区间内的数都加一或者都减一。
,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。

输入描述:

第一行一个正整数n。
接下来n行,每行一个整数,第i+1行的整数表示aia_iai。

输出描述:

第一行输出最少操作次数。
第二行输出最终能得到多少种结果。

示例1

输入

1
2
2

输出

1
2

备注:

对于100%的数据,n=100000,0≤ai<21474836480 \leq ai \lt 21474836480≤ai<2147483648

思路
参考博客:https://shineeternal.blog.csdn.net/article/details/101078881

为什么要让 d[2]~d[n] 为0,因为对差分数组进行前缀和的话,只有d[2]~d[n]为0,计算的结果才相等。
question: 所以我们要进行多少次操作呢? max( 正数的和,负数绝对值的和) 为什么呢?

因为你每次在原数组的操作是一个区间[l,r] 体现在差分数组上面就是d[l]+1 d[r+1]-1(或者d[l]-1 d[r+1]+1)
因为每次操作只能对一个正数-1 一个负数+1 所以需要 min( 正数的和,负数绝对值的和)
如果还有剩余的正数或者负数 我们视为[l,l]的+1 或者 -1 即为它们的差值 abs(正数的和 - 负数绝对值的和).
min( 正数的和,负数绝对值的和)+abs(正数的和 - 负数绝对值的和) = max( 正数的和,负数绝对值的和)

question:第二问怎么计算呢?
也就是更改首位元素的值的方案
接着上一问的一个线索,假如还有剩余的正数。
比如当前的差分序列 为 d[1]:4 d[2]:0 d[3]:2 d[4]:0
那么我们就有两种选择,要不自己消掉,要不和d[1]对消。
即自己对消的话 d[1]:4
和d[1]对消1次 d[1]:5
和d[1]对消2次 d[1]:6
所以就是 abs(正数的和 - 负数绝对值的和)+1

#include<stdio.h>
#include<iostream>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
const int maxn=1e5+5;
int Sequence[maxn];
int main()
{
    int T;
    cin>>T;
    long long pos;
    long long neg;
    pos=0;
    neg=0;
    for(int i=1;i<=T;i++)
        cin>>Sequence[i];
    for(int i=2;i<=T;i++){
        if(Sequence[i]-Sequence[i-1]>0)
            pos+=Sequence[i]-Sequence[i-1];
        else
            neg-=Sequence[i]-Sequence[i-1];
    }
    cout<<max(pos,neg)<<endl<<abs(pos-neg)+1<<endl;
}
3.C Tallest Cow

链接:https://ac.nowcoder.com/acm/contest/999/C
来源:牛客网

题目描述

FJ’s N (1 ≤ N ≤ 10,000) cows conveniently indexed 1…N are standing in a line. Each cow has a positive integer height (which is a bit of secret). You are told only the height H (1 ≤ H ≤ 1,000,000) of the tallest cow along with the index I of that cow.
FJ has made a list of R (0 ≤ R ≤ 10,000) lines of the form “cow 17 sees cow 34”. This means that cow 34 is at least as tall as cow 17, and that every cow between 17 and 34 has a height that is strictly smaller than that of cow 17.
For each cow from 1…N, determine its maximum possible height, such that all of the information given is still correct. It is guaranteed that it is possible to satisfy all the constraints.

输入描述:

Line 1: Four space-separated integers: N, I, H and R
Lines 2..R+1: Two distinct space-separated integers A and B (1 ≤ A, B ≤ N), indicating that cow A can see cow B.

输出描述:

Lines 1..N: Line i contains the maximum possible height of cow i.

示例1

输入

9 3 5 5
1 3
5 3
4 3
3 7
9 8

输出

5
4
5
3
4
4
5
5
5

题意描述:

FarmerJohn 有n头牛,它们按顺序排成一列。 FarmerJohn 只知道其中最高的奶牛的序号及它的高度,其他奶牛的高度都是未知的。现在 FarmerJohn 手上有RRR条信息,每条信息上有两头奶牛的序号(a和b),其中b奶牛的高度一定大于等于a奶牛的高度,且a,b之间的所有奶牛的高度都比a小。现在FarmerJohn想让你根据这些信息求出每一头奶牛的可能的最大的高度。(数据保证有解)

输入格式:
第1行:四个以空格分隔的整数:n,i,h和R(n和R意义见题面; i 和 h 表示第 i 头牛的高度为 h ,他是最高的奶牛)

接下来R行:两个不同的整数a和b(1 ≤ a,b ≤ n)

输出格式:
一共n行,表示每头奶牛的最大可能高度.

思路:

两个可以望见的牛之间的牛的身高是严格小于的,所以两头牛之间的身高都要减1,用前缀和维护
有可能会出现重复的数据,要进行判断重复

#include<stdio.h>
#include<iostream>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<map>

using namespace std;
const int maxn=1e5+5;
map<pair<int,int>,bool> book;
int Sequence[maxn];
int main()
{
    int n,i,h,r;
    cin>>n>>i>>h>>r;
    int x,y;
    for(int j=1;j<=r;j++){
        cin>>x>>y;
        if(x>y) swap(x,y);
        if(book[make_pair(x,y)]) continue;
        book[make_pair(x,y)]=1;
        Sequence[x+1]--;
        Sequence[y]++;
    }
    for(int j=1;j<=n;j++){
        Sequence[j]=Sequence[j-1]+Sequence[j];
        cout<<Sequence[j]+h<<endl;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++六子棋博弈算法代码的实现需要用到博弈树和极大极小值算法。具体实现步骤如下: 1.定义棋盘数据结构,用二维数组来表示棋盘,0表示空位,1表示黑子,2表示白子。 2.定义玩家和电脑的棋子颜色,用变量来表示。 3.定义胜利条件,当有一方连成六子即可获胜。 4.实现落子函数,判断当前位置是否为空,如果是,则在该位置落子,并判断是否有一方获胜。 5.实现博弈树生成函数,从当前局面开始,归生成所有可能的下一步局面,并记录每个局面的得分。 6.实现极大极小值算法,根据博弈树的得分计算出当前最优的落子位置。 7.最后,实现主函数,调用以上函数,实现完整的六子棋博弈算法。 这里给出一个参考代码供您参考: ``` #include<iostream> #include<cstring> using namespace std; const int MAXN = 15; const int INF = 0x3f3f3f3f; int mp[MAXN][MAXN]; int color, oppo_color; bool is_win(int x, int y) { // 判断是否赢了 int cnt = 1; for(int i = x + 1; mp[i][y] == color && i <= 10; i++) cnt++; for(int i = x - 1; mp[i][y] == color && i >= 1; i--) cnt++; if(cnt >= 6) return true; cnt = 1; for(int i = y + 1; mp[x][i] == color && i <= 10; i++) cnt++; for(int i = y - 1; mp[x][i] == color && i >= 1; i--) cnt++; if(cnt >= 6) return true; cnt = 1; for(int i = x + 1, j = y + 1; mp[i][j] == color && i <= 10 && j <= 10; i++, j++) cnt++; for(int i = x - 1, j = y - 1; mp[i][j] == color && i >= 1 && j >= 1; i--, j--) cnt++; if(cnt >= 6) return true; cnt = 1; for(int i = x + 1, j = y - 1; mp[i][j] == color && i <= 10 && j >= 1; i++, j--) cnt++; for(int i = x - 1, j = y + 1; mp[i][j] == color && i >= 1 && j <= 10; i--, j++) cnt++; if(cnt >= 6) return true; return false; } int eval() { // 计算当前局面得分 int score = 0; for(int i = 1; i <= 10; i++) { for(int j = 1; j <= 10; j++) { if(mp[i][j]) { int cnt = 0; for(int k = i + 1; mp[k][j] == mp[i][j] && k <= 10; k++) cnt++; if(cnt >= 4) score += mp[i][j] * cnt * cnt; cnt = 0; for(int k = j + 1; mp[i][k] == mp[i][j] && k <= 10; k++) cnt++; if(cnt >= 4) score += mp[i][j] * cnt * cnt; cnt = 0; for(int k = i + 1, l = j + 1; mp[k][l] == mp[i][j] && k <= 10 && l <= 10; k++, l++) cnt++; if(cnt >= 4) score += mp[i][j] * cnt * cnt; cnt = 0; for(int k = i + 1, l = j - 1; mp[k][l] == mp[i][j] && k <= 10 && l >= 1; k++, l--) cnt++; if(cnt >= 4) score += mp[i][j] * cnt * cnt; } } } return score; } int dfs(int depth, int alpha, int beta) { // 极大极小值算法 if(depth == 0) return eval(); int value, max_value, min_value; if(color == oppo_color) { // 玩家回合 max_value = -INF; for(int i = 1; i <= 10; i++) { for(int j = 1; j <= 10; j++) { if(!mp[i][j]) { mp[i][j] = oppo_color; if(is_win(i, j)) { // 如果这一步可以获胜,则直接返回极大值 mp[i][j] = 0; return INF; } value = dfs(depth - 1, alpha, beta); max_value = max(max_value, value); alpha = max(alpha, value); mp[i][j] = 0; if(alpha >= beta) return max_value; // 剪枝 } } } return max_value; } else { // 电脑回合 min_value = INF; for(int i = 1; i <= 10; i++) { for(int j = 1; j <= 10; j++) { if(!mp[i][j]) { mp[i][j] = color; if(is_win(i, j)) { // 如果这一步可以获胜,则直接返回极小值 mp[i][j] = 0; return -INF; } value = dfs(depth - 1, alpha, beta); min_value = min(min_value, value); beta = min(beta, value); mp[i][j] = 0; if(alpha >= beta) return min_value; // 剪枝 } } } return min_value; } } void computer_move() { // 计算电脑的落子位置 int x, y, max_score = -INF, score; for(int i = 1; i <= 10; i++) { for(int j = 1; j <= 10; j++) { if(!mp[i][j]) { mp[i][j] = color; if(is_win(i, j)) { // 如果这一步可以获胜,则直接落子 cout << "Computer move: (" << i << ", " << j << ")" << endl; mp[i][j] = color; return; } score = dfs(3, -INF, INF); // 搜索深度为3层 if(score > max_score) { max_score = score; x = i; y = j; } mp[i][j] = 0; } } } cout << "Computer move: (" << x << ", " << y << ")" << endl; mp[x][y] = color; } void print_board() { // 打印当前棋盘 cout << " "; for(int i=1;i<=10;i++) cout<<" "<<i%10<<" "; cout<<endl; for(int i=1;i<=10;i++) { cout<<i%10<<" "; for(int j=1;j<=10;j++) { if(mp[i][j]==0) cout<<" . "; else if(mp[i][j]==1) cout<<" X "; else cout<<" O "; } cout<<endl<<endl<<endl;; } } int main() { memset(mp,0,sizeof(mp)); int op; cout<<"请选择先手:"<<endl<<"1.我"<<endl<<"2.电脑"<<endl; cin>>op; if(op ==2){ color=2; oppo_color=1; computer_move(); print_board(); }else{ color=1; oppo_color=2; } while(1) { int x,y; cout<<"请下棋:"<<endl; cin>>x>>y; if(mp[x][y]) { cout<<"该位置已经有子,请重新输入!"<<endl<<endl;; continue; } mp[x][y]=oppo_color; if(is_win(x,y)) { cout<<"你赢了!"<<endl<<endl;; print_board(); break; } computer_move(); if(is_win(x,y)) { cout<<"你输了!"<<endl<<endl;; print_board(); break; } print_board(); } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值