声明:本人水平有限,欢迎各位大佬来打脸( ̄ε(# ̄)
【题意】
n个数,在相同的数中只保留出现最晚的那个,并将剩下的数按原顺序的先后输出。
【思路】
ai≤1000 a i ≤ 1000 ,所以可以开一1000的数组,记录每个数值的最后一次出现的位置,然后按位置排序输出对应的数值。
【代码】
#include
#include
using std::sort;
#define N_max 100005
int n;
struct thing {
int v;
int id;
thing():v(0),id(0){}
}loc[1002];
int cmp(thing t1, thing t2) {
return t1.id < t2.id;
}
int main() {
scanf("%d", &n);
int ipt;
for (int i = 1; i <=n; ++i) {
scanf("%d", &ipt);
loc[ipt].id = i;
loc[ipt].v = ipt;
}
sort(loc, loc + 1002,cmp);
int i; for (i = 0; loc[i].id == 0; ++i);
printf("%d\n", 1002 - i);
for (; i < 1002; ++i)
printf("%d%c", loc[i].v,i==1001?'\n':' ');
}
【题意】
一个字符串,每次处理可以删除一个’x’,求至少几次操作使得字符串中没有”xxx”子串。
【思路】
对于每一个长度≥3的”xx…xx”子串,消除它需要的操作次数是它的长度 −2 − 2 ,只要消除了所有的子串就可以结束了。
【代码】
#include
#include
using std::sort;
#define N_max 100005
int n;
char ipt[102];
int cnt,ans;
int main() {
scanf("%d", &n);
cnt = 0; //当前子串含有'x'的个数
ans = 0;//总操作次数
int i;
scanf("%c", &ipt[101]);//多余回车
for (i = 0; i scanf("%c", &ipt[i]);
if (ipt[i] == 'x')cnt++;
else {
//"x..x"子串结束,将它消除掉
ans += (cnt - 2>0?cnt-2:0);
cnt = 0;
}
}
//末尾还需要单独检测
if (ipt[i] == 'x')cnt++;
else {
ans += (cnt - 2>0 ? cnt - 2 : 0);
cnt = 0;
}
printf("%d\n", ans);
}
【题意】
有n个公寓,每个公寓分别有 ai a i 个房间,现在把1~n号公寓的房间统一编号为1~ a1+a2+...+an a 1 + a 2 + . . . + a n 。输入m个编号,求这些编号对应的是第几个公寓的第几个房间。
【思路】
统一编号以后,每个房间所处的区间范围都确定了,分别是 [1,a1] [ 1 , a 1 ] , [a1+1,a1+a2] [ a 1 + 1 , a 1 + a 2 ] , [a1+a2+1,a1+a2+a3] [ a 1 + a 2 + 1 , a 1 + a 2 + a 3 ] , … 只要找到了房间的编号在哪两个区间的起点之间,就确定了它在哪个公寓,而求它是第几个房间只需要减去它所在的区间的起点就行了。
【注意】
公寓数量高达2e5,由于是连续编号,区间的起点是递增的,所以可以使用二分查找属于哪个区间。
编号范围是1e10,需要用long long 来存储。
【代码】
#include
typedef long long ll;
ll a[200005];
int n, m;
int main() {
scanf("%d %d", &n,&m);
a[0] = 0;
//输入预处理,保存的是区间的起点
for (int i = 1; i <=n; ++i)
{
scanf("%lld", &a[i]);
a[i] = a[i] + a[i - 1];
}
ll let;
for (int i = 1; i <= m; ++i) {
scanf("%lld", &let);
int l = 0,r=n+1,mid;
//二分查找区间起点
while (l + 1 < r) {
mid = ((l + r) >> 1);
if (a[mid] < let)l = mid;
else r = mid;
}
//查询的结果是它右边最近的区间起点
//它是第几个房间:编号-前一个区间起点
printf("%d %lld\n", r, let - a[r-1]);
}
}
【题意】
输入一个数字序列,对于每一个数只能进行一次[加1/减1/不操作]处理,问能否把序列处理为等差序列。
【分析】
直接判断并不好做,转而分析解空间。
根据等差数列的性质,如果首项、尾项和个数都知道了,那么这个数列就确定了。在这道题中,个数都是n,而由于操作的限制,首项只可能为 {a1−1,a1,a1+1} { a 1 − 1 , a 1 , a 1 + 1 } ,尾项只可能为 {an−1,an,an+1} { a n − 1 , a n , a n + 1 } 。
那么也就意味着原数列有可能变为的等差数列只有九种,检查原数列能否变成这些等差数列,只需要判断每一位的差是否大于1就行了。
【思路】
分为两步:[1]生成一个等差数列,[2]检查能否处理为这个等差数列,并记录操作次数。检查结束后,选择操作次数最少的那个。
【代码】
#include
#include
using std::sort;
#define min(a,b) ((a)<(b)?(a):(b))
#define mabs(x) ((x)>0?(x):(0-(x)))
#define N_max 100005
int n;
int a[N_max];
int help[N_max];
int cnt;
int check(int a0,int an) {
//不存在这样的等差数列
if (mabs(an - a0) % (n-1) != 0)return 0;
cnt = 0;
for (int i = 1; i <= n; ++i) {
help[i] = a0 + (an - a0) / (n-1)*(i-1);
if (mabs(a[i] - help[i]) > 1)return 0;
else if (a[i] != help[i])cnt++;
}
return 1;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
}
if (n == 1) { printf("0"); return 0; }
int ck;
int flag=0, mi=100005;
//遍历所有的起点和终点
for (int l = a[1] - 1; l <= a[1] + 1; ++l) {
for (int r = a[n] - 1; r <= a[n] + 1; ++r) {
if (1 == check(l, r)) {
flag = 1;
//维护最少的操作次数
mi = min(mi, cnt);
}
}
}
if (flag == 1)printf("%d", mi);
else printf("-1");
return 0;
}
【题意】
记录n个站人数的变化的个数,求最开始的时候的人数有多少种可能性(也就是取值范围的大小)。
【思路】
假设起始是0个人,按照记录模拟一遍人数的变化,并记录下人最多和最少的个数 max和min
假设起始的人数最少是l最大是r,那么只要满足
【注意】
不懂为什么会卡long long
【代码】
#include
#include
using std::sort;
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define mabs(x) ((x)>0?(x):(0-(x)))
#define N_max 1003
typedef long long ll;
ll n,w, a[N_max];
/*
每站人数变化,记录y-x
*/
int main() {
scanf("%lld %lld", &n,&w); a[0] = 0;
ll mi=1000000009, ma=0;
for (int i = 1; i <= n; ++i)
{
scanf("%lld", &a[i]);
a[i] += a[i - 1];
mi = min(a[i], mi);
ma = max(a[i], ma);
}
ll l, r;
//构造满足不等式的l,r
if (mi < 0)l = 0 - mi;
else l = 0;
if (ma < 0)r = w;
else r = w - ma;
if (l <= r)printf("%lld", r - l + 1);
else printf("0");
}
【题意】
一个办公室有 n n 个程序员,水平为 ri r i ,如果两个程序员 a a 和 b b ,如果 a a 的水平比 b b 的水平高 (ra>rb) ( r a > r b ) ,并且两个人没有吵架,那么 a a 就可以取指导 b b 写程序。求每个程序员可以指导的人数(猿数)。
【思路】
可以指导的人数=水平更低的人数-水平低并吵架的人数
水平更低的人数:把程序员按水平排序,比他水平更低的人数就是水平比他低的人的最大排名。
水平低并吵架的人数:吵架的配对中,记录水平更低的人的数量
【代码】
#include
#include
using std::sort;
#define N_max 200005
struct thing {
int v;
int id;
thing():v(0),id(0){}
}pro[N_max];
int dis[N_max];
int cmp(thing t1, thing t2) {
return t1.v != t2.v?t1.vint cmpi(thing t1, thing t2) {
return t1.id < t2.id;
}
typedef long long ll;
int n, k;
int ipt[N_max];
int cnt1[N_max];
int main() {
scanf("%d %d", &n, &k);
for (int i = 1; i <= n; ++i) {
scanf("%d", ipt + i);
pro[i].v=ipt[i];
pro[i].id = i;
}
//按水平排序
sort(pro + 1, pro + n + 1, cmp);
int a1, a2;
//记录水平更低并吵架的人数
for (int i = 0; i < k; ++i) {
scanf("%d %d", &a1, &a2);
if (ipt[a1] > ipt[a2])
cnt1[a1]++;
else if (ipt[a2] > ipt[a1])
cnt1[a2]++;
}
for (int i = 1; i <= n; ++i) {
int l = 0, r = n + 1, mid;
//二分查找水平更低的人的排名
while (l + 1 < r) {
mid = (l + r) / 2;
if (pro[mid].v <= ipt[i] - 1)l = mid;
else r = mid;
}
//l就是水平低的人的最大排名
printf("%d%c", l - cnt1[i], i == n ? '\n' : ' ');
}
}
【题意】
一共有n天,m场考试,每场考试在第 di d i 天,可以从第 si s i 天开始复习,总共需要 ci c i 天来复习。同一天只能复习一科,参加一科考试,或者啥也不干。也就是说每场考试的可复习时间为 si s i ~ di d i -1,并且要从中选出 ci c i 天只用来复习这场考试。问能否安排好时间让每一科都复习完。如果能,输出每天复习的科目编号,如果那天是考试就输出 m m +1;如果不能,输出-1。
【思路】
这个问题的最优解应该是:所有考试的复习,都安排在与其他考试冲突最少的时间段。可以通过对时间段按 di d i 递增,优先处理早结束的考试,并且给它安排的复习时间尽可能的早;而对于同一天的考试,只要每场考试的复习都是尽早安排的,在有解的情况下,它们一定不会冲突。如果按照冲突最少的方案来分配都找不到解,那么一定不存在解。
【代码】
#include
#include
using std::sort;
#define N_max 102
//考试结构体
struct thing {
int s, d, c;
int id;
}ex[N_max];
//排序方式:按结束时间递增排序
int cmps(thing t1, thing t2) {
return t1.d != t2.d ? t1.d < t2.d : (t1.s != t2.s ? t1.s > t2.s : t1.c < t2.c);
}
int n, m;
//日期安排的记录
int vis[102];
int main() {
scanf("%d %d", &n, &m);
for (int i = 0; i < m; ++i)
{
scanf("%d %d %d", &ex[i].s, &ex[i].d, &ex[i].c);
ex[i].id = i + 1;
//考试的日子也不能冲突
if (vis[ex[i].d] == 0)vis[ex[i].d] = m + 1;
else { printf("-1"); return 0; }
}
sort(ex, ex + m, cmps);
for (int i = 0; i < m; ++i) {
//依次安排每场考试
for (int t = ex[i].s; t < ex[i].d&&ex[i].c>0; ++t) {
//遍历这场考试的可复习时间
if (vis[t] == 0) {
vis[t] = ex[i].id; ex[i].c--;
}
}
//未能找到足够的时间
if (ex[i].c > 0) { printf("-1"); return 0; }
}
for (int i = 1; i <= n; ++i) {
printf("%d%c", vis[i], i == n ? '\n' : ' ');
}
}
【总结】
思路一定要清楚,时刻着眼于解空间的构成来抓住问题的核心