时间限制:3000MS
内存限制:589824KB
小美在玩一项游戏。该游戏的目标是尽可能抓获敌人。
敌人的位置将被一个二维坐标(x, y)所描述。
小美有一个全屏技能,该技能能一次性将若干敌人一次性捕获。
捕获的敌人之间的横坐标的最大差值不能大于A,纵坐标的最大差值不能大于B。
现在给出所有敌人的坐标,你的任务是计算小美一次性最多能使用技能捕获多少敌人。
第一行三个整数N,A,B,表示共有N个敌人,小美的全屏技能的参数A和参数B。
接下来N行,每行两个数字x,y,描述一个敌人所在的坐标。
1≤N≤500,1≤A,B≤1000,1≤x,y≤1000。
一行,一个整数表示小美使用技能单次所可以捕获的最多数量。
3 1 1
1 1
1 2
1 3
2
思路:这道题如果直接在二维数组中标点并进行统计的话,由于图坐标范围比较大,因此不太可靠,同时点数相对二维图的大小来说过于少了。换个思路,其实只需要对每个点统计这个点作为左上、左下、右上、右下点时包含的数量即可,每次加入第i个点的时候,和前面i-1个点计算一下并维护一下各自的四个值。举例来说,如果一个点i作为左上角时包含一个点t,那么t作为右下角时一定也会包含点i,当然值得注意的是这个不能说明点i作为左上角和点t作为右下角对应的矩形是相同的,只能说各自在对方的包含域里而已。
#include
#include
using namespace std;
struct Point{
int x, y, left_up_num, left_down_num, right_up_num, right_down_num;
};
const int maxn = 5e2 + 5;
Point points[maxn];
int main(){
int N, A, B, x, y, idx, pre_idx, max_num = 0, diff_x, diff_y;
cin >> N >> A >> B;
for(idx = 0; idx < N; ++ idx){
cin >> x >> y;
points[idx] = Point{x, y, 1, 1, 1, 1};
for(pre_idx = 0; pre_idx < idx; ++ pre_idx){
diff_x = points[idx].x - points[pre_idx].x;
diff_y = points[idx].y - points[pre_idx].y;
if(abs(diff_x) > A || abs(diff_y) > B){
continue;
}
//由于存在共线即diff_x = 0或diff_y = 0的情况 因此不能直接else
if(diff_x <= 0 && diff_y <= 0){// 当前点作为左上角 对应点作为右下角
//cout << "(" << points[idx].x << "," << points[idx].y << ")" << "作为左上角 包含(" << points[pre_idx].x << "," << points[pre_idx].y << ")" << endl;
++ points[idx].left_up_num;
++ points[pre_idx].right_down_num;
max_num = max(max_num, max(points[idx].left_up_num, points[pre_idx].right_down_num));
}
if(diff_x >= 0 && diff_y <= 0){// 当前点作为左下角 对应点作为右上角
//cout << "(" << points[idx].x << "," << points[idx].y << ")" << "作为左下角 包含(" << points[pre_idx].x << "," << points[pre_idx].y << ")" << endl;
++ points[idx].left_down_num;
++ points[pre_idx].right_up_num;
max_num = max(max_num, max(points[idx].left_down_num, points[pre_idx].right_up_num));
}
if(diff_x <= 0 && diff_y >= 0){// 当前点作为右下角 对应点作为左下角
//cout << "(" << points[idx].x << "," << points[idx].y << ")" << "作为右下角 包含(" << points[pre_idx].x << "," << points[pre_idx].y << ")" << endl;
++ points[idx].right_down_num;
++ points[pre_idx].left_up_num;
max_num = max(max_num, max(points[idx].right_down_num, points[pre_idx].left_up_num));
}
if(diff_x >= 0 && diff_y >= 0){// 当前点作为右上角 对应点作为左下角
//cout << "(" << points[idx].x << "," << points[idx].y << ")" << "作为右上角 包含(" << points[pre_idx].x << "," << points[pre_idx].y << ")" << endl;
++ points[idx].right_up_num;
++ points[pre_idx].left_down_num;
max_num = max(max_num, max(points[idx].right_up_num, points[pre_idx].left_down_num));
}
}
//cout << "左上 左下 右上 右下" << endl;
for(pre_idx = 0; pre_idx <= idx; ++ pre_idx){
cout << " " << points[idx].left_up_num << " " << points[idx].left_down_num << " " << points[idx].right_up_num << " " << points[idx].right_down_num << endl;
}
}
cout << max_num;
}
时间限制:3000MS
内存限制:589824KB
小美现在有一串彩带,假定每一厘米的彩带上都是一种色彩。
因为任务的需要,小美希望从彩带上截取一段,使得彩带中的颜色数量不超过K种。
显然,这样的截取方法可能非常多。于是小美决定尽量长地截取一段。
你的任务是帮助小美截取尽量长的一段,使得这段彩带上不同的色彩数量不超过K种。
第一行两个整数N,K,以空格分开,分别表示彩带有N厘米长,你截取的一段连续的彩带不能超过K种颜色。
接下来一行N个整数,每个整数表示一种色彩,相同的整数表示相同的色彩。1≤N,K<5000,彩带上的颜色数字介于[1,2000]之间。
一行,一个整数,表示选取的彩带的最大长度。
8 3
1 2 3 2 1 4 5 1
5
思路:我们对于每一种颜色记录该颜色上一次出现的位置,初始的时候,所有颜色的上一次出现位置为0。我们每次读取一个新的颜色,并判断当前颜色是否在已包含的颜色集合里,如果不存在,则增加一个颜色数量,同时判断数量是否超过K,如果超过,则在已有颜色里找到上一次出现位置最靠前的踢出集合,并将区间左端点(彩带最左端的前一个位置)移动到该位置,同时维护答案。
#include
#include
时间限制:3000MS
内存限制:589824KB
现在小美获得了一个字符串。小羊想要使得这个字符串是回文串。
小美找到了你。你可以将字符串中至多两个位置改为任意小与英文字符'a'-'z'。
你的任务是帮助小美在当前制约下,获得字典序最小的回文字符串。
数据保证能在题目限制下形成回文字符串。
注:回文字符串:即一个字符串从前向后和从后向前是完全一致的字符串。
例如字符串ababa, aaaa,acca都是回文字符串。字符串abed, acea都不是回文字符串
一行,一个字符串,字符串中仅由小写英文字符构成。
保证字符串不会是空字符串。
字符串长度介于[1, 100000]之间。
一行,一个在题目条件限制下所可以获得的字典序最小的回文字符串。
acca
aaaa
思路:字符串,扫一遍看多少个位置不同
A、如果没有位置不同,从头开始尝试变a,如果头已经是a,就考虑下一个,以此类推。
B、如果只有一个位置不同,考虑前面部分的是不是a
1、如果前面的是a,把后面的变a;现在还剩一次改动机会,需要考虑串的字符数是奇数还是偶数。如果是奇数,考虑把中心字符改为a;如果为偶数,则没法动,因为如果想把下一位改a,由于它们已经回文,要么本来就是a无需更改,要么两个相等且不为a,要同时改两个。
2、如果前面的不是a,把前面的改为a后,若后面对应位置的也不为a,则后面的也改为a,用完两次机会。否则在为奇数串的情况下改中心字符为a。
总结来说:优先把不同的位置都变为a,如果有多余的机会,看是否可以改中心。
C、如果有两个位置不同,对于每个不同的位置,考虑把字典序大的改成小的,因为如果前面的字典序大,改小更好,如果前面的字典序小,那只能选择把后面的改小。
#include
#include
#include
using namespace std;
int main(){
string str;
cin >> str;
int str_len = str.length(), left = 0, right = str_len - 1, not_equal_num = 0, idx;
vector not_equal_idx;
while(left < right){
if(str[left] != str[right]){
++ not_equal_num;
not_equal_idx.push_back(left);
not_equal_idx.push_back(right);
}
++ left;
-- right;
}
if(not_equal_num == 0){//没有不同
left = 0, right = str_len - 1;
while(left < right){
if(str[left] != 'a'){
str[left] = str[right] = 'a';
break;
}
++ left;
-- right;
}
}else if(not_equal_num == 1){//1个不同
left = not_equal_idx[0];
right = not_equal_idx[1];
str[left] = str[right] = min(str[left], str[right]);
if(str_len & 1){
str[str_len >> 1] = 'a';
}
}else{//2个不同
for(idx = 0; idx <= 2; idx += 2){
left = not_equal_idx[idx];
right = not_equal_idx[idx + 1];
str[left] = str[right] = min(str[left], str[right]);
}
}
cout << str;
return 0;
}
时间限制:3000MS
内存限制:589824KB
现在商店里有N个物品,每个物品有原价和折扣价。
小美想要购买商品。小美拥有X元,一共Y张折扣券。
小美需要最大化购买商品的数量,并在所购商品数量尽量多的前提下,尽量减少花费。
你的任务是帮助小美求出最优情况下的商品购买数星和花费的钱数。
第一行三个整数,以空格分开,分别表示N,X,Y.
接下来N行,每行两个整褛数,以空格分开,表示一个的原价和折扣价。
1≤Ns100,1≤X≤5000,1≤Y≤50,每个商品原价和折扣价均介于[1,50]之间。
一行,两个整数,以空格分开。第一个数字表示最多买几个商品,第二个数字表示在满足商品尽的前提下所花费的最少的钱数。
3 5 1
4 3
3 1
6 5
2 5
3 5 1
4 3
3 1
6 1
2 4
10 30 3
2 1
3 2
2 1
10 8
6 5
4 3
2 1
10 9
5 4
4 2
8 24
思路:本题给的物品价格并不大,应该是dp没跑了。设dp[idx][choiced_num][discount_num]表示 在已考虑idx件物品物品、已选定choiced_num件物品、已使用discount_num个折扣的情况下的最少花费,按照如下进行分析:
A.discount_num=0,表示当前未使用过折扣,那么前面也不能使用折扣,同时考虑到当前这件物品是否被购买(如有购买,则必须全额),有如下转移方程
B.discount_num!=0,表示当前有使用过折扣,考虑当前物品的买与不买
1.当前物品不买时,为如下状态
2.当前物品购买时,分为两种,一种是前面用了discount_num-1次折扣,本次使用折扣;一种是前面用了discount_num次折扣,本次只能全额
#include
#include
using namespace std;
const int maxn = 1e2 + 5;
const int maxx = 5e3 + 5;
int ori_price[maxn], discount_price[maxn], dp[maxn][maxn][55]; //i:已考虑的物品数 j:已选定物品数量 k:已使用折扣数量 dp[i][j][k]:约束下的最少花费
int main(){
int N, X, Y, idx, choiced_num, discount_num, max_buy_num = 0, min_buy_cost;
cin >> N >> X >> Y;
memset(dp, 0x3f, sizeof(dp));
dp[0][0][0] = 0;
min_buy_cost = X;
for(idx = 1; idx <= N; ++ idx){
cin >> ori_price[idx] >> discount_price[idx];
dp[idx][0][0] = 0;//考虑idx件物品,一件都没选,也没用折扣
for(choiced_num = 1; choiced_num <= idx; ++ choiced_num){
for(discount_num = 0; discount_num <= min(Y, choiced_num); ++ discount_num){
if(discount_num == 0){//如果当前状态是未使用折扣折扣,那么之前也必须没用过,并且 要么本次没买,要么本次是全额购买
dp[idx][choiced_num][0] = min(dp[idx - 1][choiced_num][0], dp[idx - 1][choiced_num - 1][0]+ ori_price[idx]);
//cout << "idx:" << idx << " choiced_num:" << choiced_num << " dis_num:" << 0 << endl;
//cout << "要么本次没买:" << dp[idx - 1][choiced_num][0] << " 本次是全额购买:" << dp[idx - 1][choiced_num - 1][0]+ ori_price[idx] << endl;
} else{//如果当前状态是使用折扣 那么要么本次没用折扣,之前已经用了discount_num次,要么之前用了discount_num-1次,本次原价购买
dp[idx][choiced_num][discount_num] = min(dp[idx - 1][choiced_num - 1][discount_num]+ ori_price[idx], dp[idx - 1][choiced_num - 1][discount_num - 1]+ discount_price[idx]);
//
dp[idx][choiced_num][discount_num] = min(dp[idx][choiced_num][discount_num], dp[idx - 1][choiced_num][discount_num]);
}
if(dp[idx][choiced_num][discount_num] <= X && max_buy_num <= choiced_num){//想要花的少又买的东西多
if(max_buy_num == choiced_num){//如果买的数量一样 取花费少的
min_buy_cost = min(min_buy_cost, dp[idx][choiced_num][discount_num]);
} else if(max_buy_num < choiced_num){//如果买的数量多 直接换到数量多的
min_buy_cost = dp[idx][choiced_num][discount_num];
max_buy_num = choiced_num;
}
}
//cout << "考虑的物品数:" << idx << " 已选定的数量:" << choiced_num << " 已使用折扣数量:" << discount_num << " 最少花费:" << dp[idx][choiced_num][discount_num] << endl;
}
}
}
cout << max_buy_num << " " << (max_buy_num == 0 ? 0 : min_buy_cost); //如果一件都买不起 花费也肯定是0
return 0;
}