【题目】有一种带缺陷的导弹拦截系统,它的第一发炮弹能够到达任意的高度,之后每一发炮弹都不能超过前一发的高度。现给出导弹依次飞来的高度,计算一下最少需要多少套拦截系统。
【思路】
对每个炮弹进行处理:
当前高度低于或等于其中一些系统:选择这些系统中高度最低的,该系统高度=当前高度
当前高度高于所有系统:系统数+1,该系统高度=当前高度
#include
#define INF 30005
int main(){
int n;
while(scanf("%d",&n)!=EOF){
int t,hm,i,im=0,s[105]={0},mi;
while(n--){
scanf("%d",&t);
hm=INF;
for(i=1;i<=im;i++)
if(s[i]>=t&&s[i]<hm)
{hm=s[i];mi=i;}
if(hm<INF) s[mi]=t;
else {im++;s[im]=t;}
}
printf("%d\n",im);
}
return 0;
}
【题目】玩家在游戏里有n种属性,每种属性都有m(1≤n, m≤1000)个等级,编号1~m。在开始的时候,每种属性都处于等级0。当第i种属性在第j-1等级时,玩家可以花c[i][j] (-109≤c[i][j]≤109)的代价来把它升级到第j等级。当所有属性都升级到等级j时,玩家可以得到d[j] (-109≤d[j]≤109)的满级奖励。求玩家可以获得的最大收益。
【思路】
想法:外循环从0到m枚举所有属性的最低等级j,中循环为每种属性,内循环枚举其升到j~m级的代价,找出最小代价。
漏洞:如果所有属性都在j+1(也可以是j+2,…,m)级取得最小代价,那么就会违背所有属性的最低等级为j的前提,答案就会错误。
修正:在所有属性取得最小代价之后,选择一种属性降级到j,这样会使总代价变大,称降级带来的代价变化为“损失”,选择损失最小的属性降级。在中循环执行时统计每种属性的降级损失。
#include
#define LL long long
const int N=1000+5;
LL inf=1e9*N;
LL cc[N][N],dd[N];
int main(){
int t,ca;
scanf("%d",&ca);
for(t=1;t<=ca;t++){
int i,j,k,temp;LL pre,ma;
int n,m;//n为属性种数,m为最高等级
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
pre=cc[i][0]=0;
for(j=1;j<=m;j++){
scanf("%d",&temp);
pre=pre-temp;
cc[i][j]=pre;//cc[i][j]为单独把属性i升到j级的代价
}
}
pre=dd[0]=0;
for(j=1;j<=m;j++){
scanf("%d",&temp);
pre=pre+temp;
dd[j]=pre;//dd[j]为把所有属性升级到j级的累计满级奖励
}
LL loss,sum,ans=0;
for(j=0;j<=m;j++){//最低的等级为j
sum=dd[j];
loss=inf;
for(i=1;i<=n;i++){
sum+=cc[i][j];
ma=0;
for(k=j+1;k<=m;k++)
if(cc[i][k]-cc[i][j]>ma) ma=cc[i][k]-cc[i][j];
sum+=ma;
if(ma<loss) loss=ma;
}
sum-=loss;
if(sum>ans) ans=sum;
}
printf("Case #%d: %lld\n",t,ans);
}
return 0;
}
【题目】输入电视节目时间表,合理安排观看顺序,输出最多能完整看到的电视节目的个数。
【思路】最好的选择是剩余可安排时间最多,即结束最早。对电视节目按照结束时间排序,按播出时间从早到晚选择节目,如果时间不重叠,则入选。
【证明】如果按开始时间排序,有可能开始时间早、持续时间长、单个,比不上开始时间晚、持续时间短、多个,如1-6,2-4,4-5。反之,如果按结束时间排序,不可能出现结束时间早、持续时间长、单个。
#include
#include
#include
using namespace std;
struct jiemu
{int s;int e;}jm[105];
bool cmp(struct jiemu x,struct jiemu y)
{if(x.e!=y.e)return x.e<y.e;
return x.s>y.s;}
int main(){
int n,i,s,t;
while(scanf("%d",&n)!=EOF&&n){
for(i=0;i<n;i++)
{cin>>jm[i].s;cin>>jm[i].e;}
sort(jm,jm+n,cmp);
s=0;t=0;
for(i=0;i<n;i++)
if(jm[i].s>=t)
{t=jm[i].e;s++;}
cout<<s<<endl;
}
return 0;
}
【题目】在 n∗n 的棋盘上放 n(n≤5000)个车,使得任意两个车不相互攻击,且第 i 个车在一个给定的矩形 Ri之内(包括边界)。 用 4个整数 xli,yli,xri,yri描述第 i个矩形,其中(xli,yli)是左上角坐标,(xri,yri)(xri,yri)是右下角坐标。如果无解,输出 IMPOSSIBLE; 否则,输出 n行,依次为第 1,2,…,n1,2,…,n 个车的坐标。
【思路】
N皇后问题中由于存在对角线(主、副)约束,使用DFS较为简便;本问题中每个车的可用区域为矩形,最坏情况为
∏ i = 1 n ( x r i − x l i ) ( y r i − y l i ) \prod_{i=1}^{n}{(x_{ri}-x_{li})(y_{ri}-y_{li})} i=1∏n(xri−xli)(yri−yli)
考虑使用贪心产生不冲突方案。将行、列分开处理,以棋盘的每行(列)作为主体和顺序。
想法一:将区间按照左端点从小到大排序,然后对每个区间,从左到右找到第一个未被放置的点。如果大区间包含小区间,而大区间先被处理,取到大区间和小区间相交的地方,后续处理小区间时,小区间内所有点可能已被之前的大区间取完。所以在这种情况下,需要优先处理小区间。
想法二:把区间按照右端点从小到大排序,然后对每个区间,从左到右找到第一个未被放置的点。这样,确保越靠右的区间处理次序越靠后。
#include
#include
#include
using namespace std;
const int N=5000;int n;
int bx[5010],by[5010];
//存储棋盘上占用第i行(列)的车的编号,0代表空
struct r//存储格子编号和端点
{int l,r,num;}rx[N+10],ry[N+10];
bool cmp(r a,r b)
{if(a.r!=b.r) return a.r<b.r;
else return a.l<b.l;}
bool solve(){
for(int i=1;i<=n;i++){//对于排序后的每个格子
int j=rx[i].l;
while(bx[j]) j++;
if(j>rx[i].r) return false;
else bx[j]=rx[i].num;
}
for(int i=1;i<=n;i++){
int j=ry[i].l;
while(by[j]) j++;
if(j>ry[i].r) return false;
else by[j]=ry[i].num;
}
return true;
}
void print(){
int ans[N+10][2]={0};
for(int i=1;i<=n;i++)
{ans[bx[i]][0]=i;ans[by[i]][1]=i;}
for(int i=1;i<=n;i++)
printf("%d %d\n",ans[i][0],ans[i][1]);
}
int main(){
while(scanf("%d",&n)!=EOF&&n){
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&rx[i].l,&ry[i].l,&rx[i].r,&ry[i].r);
rx[i].num=ry[i].num=i;
}
sort(rx+1,rx+n+1,cmp);sort(ry+1,ry+n+1,cmp);
memset(bx,0,sizeof(bx));memset(by,0,sizeof(by));
if(solve()) print();
else printf("IMPOSSIBLE\n");
}
return 0;
}
【题目】肥鼠准备了M磅猫粮,准备和谷仓的看守猫交换Java豆。谷仓有N个房间,第i个房间储藏着J[i]磅Java豆,全部交换需要F[i]磅猫粮。肥鼠可以按比例与猫交易。输入房间的信息,求肥鼠最多可以得到多少Java豆。
【思路】题目要达到的最终目的是用有限的猫粮交换到尽可能的Java豆,即投入产出比最大化。进一步分解,可以求出每个房间的投入产出比,进行降序排列,求得最优解。
#include
#include
using namespace std;//使用了C++头文件
struct room{
double bean;
double qian;
double bili;
}r[1005];
bool cmp(room x,room y)
{return x.bili>y.bili;}//降序排列
int main(){
int m,n,i;
double sum;
while(scanf("%d%d",&m,&n)&&m!=-1&&n!=-1){
for(i=0;i<n;i++)
{scanf("%lf%lf",&r[i].bean,&r[i].qian);
r[i].bili=r[i].bean/r[i].qian;
//单位猫粮可以得到的Java豆}
sort(r,r+n,cmp);
sum=0.0;
for(i=0;i<n;i++){
if(m>r[i].qian) //全部交换
{sum+=r[i].bean;m-=r[i].qian;}
else {sum+=m*r[i].bili;break;}
}
printf("%.3lf\n",sum);
}
return 0;
}
【题目】现代的田忌赛马规则如下:田忌与齐王赛马,赢一场可以得到200元钱,输一场则要损失200元钱。双方各有最多1000匹马,输入各对马匹的速度,输出田忌最多可以赢的钱。
【思路】要达到利益最大化,就要使每对匹配最值,即在一定条件下,以最大差距输,以最小差距赢。为此要先对双方马匹按速度排序。
#include
#include
int main(){
int i,n,a[1005],b[1005],amin,amax,bmin,bmax,s;
while(cin>>n && n){
for(i=1;i<=n;i++) cin>>a[i];//a代表田忌
for(i=1;i<=n;i++) cin>>b[i]; //b代表齐王
sort(a+1,a+n+1);
sort(b+1,b+n+1);// 田忌与齐王的马从慢到快排序
amin=1;amax=n;
bmin=1;bmax=n;
s=0;
if(a[amax]>b[bmax])//先对付最快的
{s+=200;amax--;bmax--;}//用最快直接打(比最快慢的也许可行,最快省事)
else if(a[amax]<b[bmax]) //用最慢直接打
{s-=200;amin++;bmax--;}
else//反向对付最慢的
if(a[amin]>b[bmin]) //用最慢直接打
{s+=200;amin++;bmin++;}
else{//用最慢打最快
if(a[amin]<b[bmin]) s-=200;
//平局钱不变
amin++;bmax--;
}
cout>>s>>endl;
}
return 0;
}
【题目】在一个狭窄的走廊里将桌子从一个房间移动到另一个房间,走廊的宽度只能允许一张桌子通过。现在要移动n张桌子,每移动一张桌子需要花10分钟,移动过程中起点、终点与途经的房间不可用。输入移桌任务,输出最少花费的时间。
【思路】用数组P的下标,表示楼道的格子,400个房间对应200个格子。用s表示起点房间,d表示终点房间,则(s-1)/2是开始格子,(d-1)/2是目的格子。从开始格子到目的格子,每个格子的值都加1,表示桌子经过这些房间1次,占用一次时间。每经过一次都加1,格子值即为占用次数。如果每张按照路线中的最小值确定搬运时间,则用时最短。所以,最后把最大的值找出来乘以10就得到最短时间。
#include
using namespace std;
int main() {
int t,i,j,N,P[200];//P存储各个楼道格子的值
int s,d,temp,k,min;
cin>>t;
for(i=0;i<t;i++) {
for(j=0;j<200;j++) //P初始化
P[j]=0;
cin>>N;
for(j=0;j<N;j++) {
cin>>s>>d;
s=(s-1)/2;
d=(d-1)/2;
if(s>d)//循环变量递增,要确保起点<终点
{temp=s;s=d;d=temp;}
for(k=s;k<=d;k++)
P[k]++;
}
min=-1;
for(j=0;j<200;j++)
if(P[j]>min)
min=P[j];
cout<<min*10<<endl;
}
return 0;
}