10.1.涂色问题
问题描述:
一个长方体由边长为 1的小正方体构成,对小正方体用两种颜色进行涂色。
涂色要求为,要求左右相邻的小正方体颜色相同。
求解颜色不同的小正方体的数目最少为多少个。
输入输出格式:
输入长方体的长、宽、高;输出颜色不同的小正方体的最少个数。
问题分析:
对于长方体而言,只要长宽高中有任何一个值为偶数。
都可将小正方体涂成数目相同的两种颜色。
当长宽高都为奇数时,三个数中两个较小数的乘积即为多出的一层。
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
long long t[10];
for(int i=0;i<3;i++)
cin>>t[i];
for(int i=0;i<3;i++)
{
if(t[i]%2==0)
{
cout<<0<<endl;
return 0;
}
}
sort(t,t+3);
long long ans=t[0]*t[1];
cout<<ans<<endl;
return 0;
}
10.2.团队聚会时间问题
问题描述:
TA团队每周都会有很多任务,有的可以单独完成,有的则需要所有人聚到一起,
开过会之后才能去做。但TA团队的每个成员都有各自的事情,
找到所有人都有空的时间段并不是一件容易的事情。
给出每位助教的各项事情的时间表,你的任务是找出所有可以用来开会的时间段。
输入格式:
第一行一个数T(T≤100),表示数据组数。
对于每组数据,第一行一个数m(2 ≤ m ≤ 20),表示TA的数量。
对于每位TA,首先是一个数n(0≤ n≤100),表示该TA的任务数。
接下来n行,表示各个任务的信息,格式如下
YYYY MM DD hh mm ss YYYY MM DD hh mm ss “some string here”
每一行描述的信息为:开始时间的年、月、日、时、分、秒;
结束时间的年、月、日、时、分、秒,以及一些字符串,描述任务的信息。
数据约定:
所有的数据信息均为固定位数,位数不足的在在前面补前导0,数据之间由空格隔开。
描述信息的字符串中间可能包含空格,且总长度不超过100。
所有的日期时间均在1800年1月1日00:00:00到2200年1月1日00:00:00之间。
为了简化问题,我们假定所有的月份(甚至2月)均是30天的,数据保证不含有不合法的日期。
注意每件事务的结束时间点也即是该成员可以开始参与开会的时间点。
输出格式
对于每一组数据,首先输出一行"Scenario #i:",i即表明是第i组数据。
接下来对于所有可以用来开会的时间段,每一个时间段输出一行。
需要满足如下规则:
1.在该时间段的任何时间点,都应该有至少两人在场。
2.在该时间段的任何时间点,至多有一位成员缺席。
3.该时间段的时间长度至少应该1h。
所有的成员都乐意一天24h进行工作。
举个例子,假如现在TA团队有3位成员,TT、zjm、hrz。
那么这样的时间段是合法的:会议开始之初只有TT和zjm,后来hrz加入了,
hrz加入之后TT离开了,此后直到会议结束,hrz和zjm一直在场。
要求:
1.输出满足条件的所有的时间段,尽管某一段可能有400年那么长。
2.时间点的格式为MM/DD/YYYY hh:mm:ss。
3.时间输出格式为"appointment possible from T0 to T1",其中T0和T1均应满足时间格式。
4.严格按照格式进行匹配,如果长度不够则在前面补前导0。
5.按时间的先后顺序输出各个时间段。
6.如果没有合适的时间段,输出一行"no appointment possible"。
7.每组数据末尾须打印额外的一行空行。
问题分析:
将时间节点投射在时间轴上,形成时间段,再判断这些时间段是否满足相应条件。
构建时间类,包含年月日小时分钟秒,实现基本的符号重载
s用于存储每个成员的事务的起始时间,
e用于存储每个成员事务的结束时间,
t用于存储所有时间点初始包含了时间范围边界点
初始化变量,输入每个成员事务的时间点,直接忽略描述信息
对t中所有时间点升序排序后,从最左侧开始进行与滑动窗口类似的操作确定空闲时间段
首先确定使空闲时间段最长的右边界,
要求时间点位于空闲时间,此时间点有空的人数至少2人且最多缺席一人
确定好右边界后,就确定了一段空闲时间段
(初始左边界是最左侧,之后每轮的左边界由上一轮确定),
判断该时间段是否满足一个小时,满足则输出该时间段。
之后确定下一轮时间段的左边界,方法与确定右边界时类似。
重复之前的操作持续向右滑动窗口
用一个标记记录是否有输出符合要求的空闲时间段,最后如果没有则输出相应信息
对于给定的时间点要确定每个成员在该点的状态,统计在该时间点有空的成员总数,
要至少2人且最多缺席一人才算是符合要求的边界。
#include <iostream>
#include <string>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
using namespace std;
struct time {
int year, month, day, hour,minute,second;
time() {}
time(int y, int mon, int d, int h, int min, int s) {
year = y, month = mon, day = d, hour = h, minute = min, second = s;
}
bool operator < (const time &b)const
{
if (year != b.year)return year < b.year;
if (month != b.month)return month < b.month;
if (day != b.day)return day < b.day;
if (hour != b.hour)return hour < b.hour;
if (minute != b.minute)return minute < b.minute;
return second < b.second;
}
bool operator > (const time &b)const { return b < *this; }
bool operator <= (const time &b)const { return !(b < *this); }
bool operator == (const time &b)const { return !(b < *this || *this < b); }
}s[25][120],e[25][120],t[4020];
int num[25], cnt, n;
time tbegin(1800, 1, 1, 0, 0, 0), tend(2200, 1, 1, 0, 0, 0);
int flag = 0;
void print(int i) {
if (t[i].month < 10)printf("0");
printf("%d/", t[i].month);
if (t[i].day < 10)printf("0");
printf("%d/", t[i].day);
printf("%d ", t[i].year);
if (t[i].hour < 10)printf("0");
printf("%d:", t[i].hour);
if (t[i].minute < 10)printf("0");
printf("%d:", t[i].minute);
if (t[i].second < 10)printf("0");
printf("%d", t[i].second);
}
//确定空闲段的右边界
bool beright(int index) {
if (index == 0)return 0;
int tot = 0;
for (int i = 1; i <= n; i++) {
if (num[i] == 0) { tot++; continue; }
if (t[index] <= s[i][1] && t[index] > tbegin) { tot++; continue; }
if (t[index] > e[i][num[i]]) { tot++; continue; }
if (t[index] == e[i][num[i]])continue;
for (int j = 1; j <= num[i]; j++) {
if (t[index] > s[i][j] && t[index] <= e[i][j])break;
if (j + 1 <= num[i] && t[index] > e[i][j] && t[index] <= s[i][j + 1]) { tot++; break; }
}
}
//至少两个人在场以及最多一人缺席
if (tot >= 2 && tot >= n - 1)return 1;
else return 0;
}
//确定空闲段的左边界
bool beleft(int index) {
if (index == cnt)return 0;
int tot = 0;
for (int i = 1; i <= n; i++)
{
if (num[i] == 0) { tot++; continue; }
if (t[index] < s[i][1]) { tot++; continue; }
if (e[i][num[i]] <= t[index] && tend > t[index]) { tot++; continue; }
for (int j = 1; j <= num[i]; j++) {
if (s[i][j] <= t[index] && e[i][j]>t[index])break;
if (j + 1 <= num[i] && e[i][j] <= t[index] && s[i][j + 1] > t[index]) { tot++; break; }
}
}
if (tot>=2 &&tot >= n - 1)return 1;
else return 0;
}
//满足1h长
bool anhour(int left, int right) {
time l=t[left], r=t[right];
if (r.year - l.year >= 2)return 1;
r.month += (r.year - l.year) * 12;
if (r.month - l.month >= 2)return 1;
r.day += (r.month - l.month) * 30;
if (r.day - l.day >= 2)return 1;
r.hour += (r.day - l.day) * 24;
if (r.hour - l.hour >= 2)return 1;
r.minute += (r.hour - l.hour) * 60;
r.second += (r.minute - l.minute) * 60;
if (r.second - l.second >= 3600)return 1;
return 0;
}
void judge(int left, int right) {
if (!anhour(left, right))return;
flag = 1;
printf("appointment possible from ");
print(left);
printf(" to ");
print(right);
printf("\n");
}
int main()
{
//cin.sync_with_stdio(false);
int count;
cin >> count;
for (int cc = 1; cc <= count; cc++)
{
memset(num, 0, sizeof(num));
memset(s, 0, sizeof(s));
memset(e, 0, sizeof(e));
memset(t, 0, sizeof(t));
cnt = 0;
flag = 0;
t[++cnt] = tbegin;
t[++cnt] = tend;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> num[i];
for (int j = 1; j <= num[i]; j++) {
cin >> s[i][j].year >> s[i][j].month >> s[i][j].day >> s[i][j].hour >> s[i][j].minute >> s[i][j].second;
cin >> e[i][j].year >> e[i][j].month >> e[i][j].day >> e[i][j].hour >> e[i][j].minute >> e[i][j].second;
t[++cnt] = s[i][j];
t[++cnt] = e[i][j];
string meiyong;
getline(cin,meiyong);
}
}
sort(t + 1, t + 1 + cnt);
printf("Scenario #%d:\n", cc);
int left = 1, right = 1;
while (left <= cnt && right <= cnt) {
right++;
if (right > cnt)break;
//找到使时间段最长的右边界
while (right <= cnt && beright(right))right++;
right--;
judge(left, right);
left = right + 1;
//确定左边界
while (left <= cnt && !beleft(left))left++;
right = left;
}
if (flag == 0)printf("no appointment possible\n");
printf("\n");
}
}
10.3.数字转化
题目描述:
东东在玩游戏“Game23”。
在一开始他有一个数字n,他的目标是把它转换成m,
在每一步操作中,他可以将n乘以2或乘以3,他可以进行任意次操作。
输出将n转换成m的操作次数,如果转换不了输出-1。
输入输出说明:
输入的唯一一行包括两个整数n和m(1<=n<=m<=5*10^8).
输出从n转换到m的操作次数,否则输出-1.
输入输出样例:
输入:120 51840 输出:7
问题分析:
递归,每次先判断,如果n==m则标记为找到,return;
否则 计数count加一,两条路,2*n、3*n(如果他们小于等于m的话);最后判断输出即可。
#include<iostream>
using namespace std;
long long n,m;
int count=0,exist=0,ans=0;
int calculate(int n,int count)
{
if(n==m)
{exist=1;ans=count;return 0;}
count++;
if(2*n<=m)
calculate(2*n,count);
if(3*n<=m)
calculate(3*n,count);
}
int main()
{
long long n1;
cin>>n1>>m;
if(n1>m)
cout<<"-1"<<endl;
calculate(n1,0);
if(exist)
cout<<ans<<endl;
else
cout<<"-1"<<endl;
}
10.4.序列的动态规划
问题描述:
东东有两个序列A和B。
他想要知道序列A的LIS和序列AB的LCS的长度。
注意,LIS为严格递增的,即a1<a2<…<ak(ai<=1,000,000,000)。
输入输出说明:
第一行两个数n,m(1<=n<=5,000,1<=m<=5,000)
第二行n个数,表示序列A
第三行m个数,表示序列B
输出一行数据ans1和ans2,分别代表序列A的LIS和序列AB的LCS的长度
问题分析:
定义状态i为以Ai为结尾得最长上升子序列方程,
第一个LIS[1]=1;因为只有一个数,无关升降;
在LIS[i]时要取[0…i)此区间内的已有的做大上升子序列,
再+1,且此子序列满足此序列最大值小于A[i]。
状态转移方程:
LIS[i]=max(LIS[i], LIS[l]+ 1);且l<i; A[l]<A[i];
ans=max(ans,LIS[i]); i=1,2...n;
假设A序列长度为n,B序列长度为m;
我们创建一个二维数组LCS[n][m]为动态转移方程。
其中LCS[i][j]代表A1,A2...Ai和B1,B2...Bj得LCS长度。
初始化:LCS[0][0]=LCS[1][0]=LCS[0][1]=0(任意序列长度为0,则LCS为0);
状态转移方程:
如果当前A,B序列得数字一样,则可以直接加入LCS:
当Ai==Bj时,LCS[i][j]=LCS[i-1][j-1]+1;
如果不一样,就取上一个状态得最优:
LCS[i][j]=max(LCS[i-1][j],LCS[i][j-1]);
#include<iostream>
#include<algorithm>
using namespace std;
int A[5010],B[5010];
int LIS[5010],LCS[5010][5010];
int n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>A[i];
for(int i=1;i<=m;i++)
cin>>B[i];
int ans1=0;
for(int i=1;i<=n;i++)
{
LIS[i]=1;//最少就他自己 一个
for(int l=1;l<i;l++)//枚举i前面所有满足条件的找最大值
{ if(A[l]<A[i])
LIS[i]=max(LIS[i],LIS[l]+1);
}
ans1=max(ans1,LIS[i]);
}
for(int j=1;j<=m;j++)
for(int i=1;i<=n;i++)
if(A[i]==B[j])
LCS[i][j]=LCS[i-1][j-1]+1;
else
LCS[i][j]=max(LCS[i-1][j],LCS[i][j-1]);
int ans2=LCS[n][m];
cout<<ans1<<" "<<ans2<<endl;
}
10.5.拿数问题
问题叙述:
给 n 个数,每一步能拿走一个数,比如拿第 i 个数, Ai = x,得到相应的分数 x,
但拿掉这个 Ai 后,x+1 和 x-1 (如果有 Aj = x+1 或 Aj = x-1 存在)
就会变得不可拿(但是有 Aj = x 的话可以继续拿这个 x)。
求最大分数。
输入输出说明:
第一行包含一个整数 n (1≤n≤105),表示数字里的元素的个数
第二行包含n个整数a1, a2, …, an (1≤ai≤105)
输出一个整数:n你能得到最大分值。
问题分析:
这里是对元素值的大小有限制:
不能选择值相邻的,
即如果选择了大小为2的元素,则序列中所有的2均可选,但是所有的1和所有的3均不能选择。
因此对数值大小进行枚举,记录最小元素的值为minn,最大元素的值为maxx.
f数组针对元素的值:fi为从minn.....i能够获取的最大分数.
开设sum数组:sumi表示值为i的元素出现的个数
初始化:f[minn]=minn*sum[minn]。
状态转移方程:f[i]=max(f[i-1],f[i-2]+i*sum[i]);
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=100010;
long long n,maxx;
long long a[maxn],sum[maxn],f[maxn];
int main()
{
memset(f,0,sizeof(f));
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
maxx=max(maxx,a[i]);
sum[a[i]]++;
}
f[1]=sum[1];
for(long long i=2;i<=maxx;i++)//枚举分数
f[i]=max(f[i-1],f[i-2]+sum[i]*i);
cout<<f[maxx]<<endl;
return 0;
}