【59.61.214.20:3001/No.2012】窈窕淑女 题解

题目描述

“关关雎鸠,在河之洲。窈窕淑女,君子好逑。”——《诗经》
T博士的大儿子阿T(阿嚏?)最近遇到了一件麻烦事。
有n个MM同时邀请阿T在8月14日上午11:30拜访她们家,可那时阿T刚比赛完,所以不能先提前拜访几个,而他又不会分身术,所以他只得分别拜访。他共有T分钟的时间可以用于拜访MM。他还安排好了拜访的顺序,对于每个MM都可以选择拜访或不拜访。对于要拜访的MM必须按阿T安排好的顺序拜访。
对于每个MM都有如下数据:
不高兴系数x,为2到9的整数。
不高兴度X,若阿T迟到t分钟到她家,则X=x^t(x的t次方);若阿T不拜访她家,则X=x^T。
拜访时间y(分钟),保证0<yi<=T,即阿T在她家停留的时间,若阿T在到了她家但停留时间不足y分钟,她的不高兴度就会变成正无穷大。
假设阿T从一个地方到另一个地方不用消耗时间,阿T希望在有限的T分钟里使所有MM的不高兴度之和最小。请告诉他这个最小值是多少。

输入

第一行是两个正整数n和T。
第1+i(1<=i<=n)有两个正整数xi和yi,分别是阿T安排的拜访顺序中第i个MM的不高兴系数和拜访时间。

输出

请只输出一行,仅一个正整数,为最小的不高兴度之和。

数据范围限制

对于20%的数据,满足1<=n<=10,1<=T<=9;
对于50%的数据,满足1<=n<=20,1<=T<=30;
对于100%的数据,满足1<=n<=20,1<=T<=180;

提示

【样例输入2】
2 30
5 16
2 16
【样例输出2】
1073741825   
(只拜访第一个MM,到她家时迟到了0分钟,不高兴度之和为5^0+2^30;若只拜访第二个MM,则不高兴度之和为2^0+5^30,显然较大)

 我们需关注一条信息:对于要拜访的MM,必须按阿T安排好的顺序拜访。这个条件保证了问题的“无后效性”,因此考虑用动态规划做题。我们可以以已处理完的MM个数为阶段,以时间为附加维度,定义数组f[i(前i个MM)][t(前t分钟)]。对于每一个阶段,如果t<T,说明前t分钟时间不够拜访这个MM,只能在同一时间之前MM的不高兴度之和的最小值中加上x^T。如果t>=T,有时间去这个MM家里玩啦,那我们计算一下,是不拜访她得到的结果较优呢,还是拜访她,但把之前拜访MM的时间让出拜访她所需的时间得到的结果较优呢,于是就能得到状态转移方程了(在下面我给出的程序里)。

由于不高兴度是乘方形式的,指数很大,后80%的数据得用高精度解决。高精度实现代码难度较高,但思维难度较低,这里不做详细介绍。总结发现,这题是简单动态规划和高精度套餐的结合,和01背包问题及其相似,所以先做01背包问题再做这道题,难度会减小很多。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct supernumber{
	int d[1001];
	int l;
	supernumber(){
		memset(d,0,sizeof(d));
		l=0;
	}
}f[21][201],point,answer;
int base[21],Time[21];
int n,total;
supernumber mul(int x,supernumber y)
{
	int temp,carry=0;
	for(int i=1;i<=y.l;i++)
	{
		temp=x*y.d[i]+carry;
		carry=temp/10;
		y.d[i]=temp%10;
	}
	while(temp)
	{
		y.d[y.l++]=temp%10;
		temp/=10;
	}
	y.l--;
	return y;
}
supernumber add(supernumber x,supernumber y)
{
	supernumber z;
	int carry=0;
	z.l=max(x.l,y.l)+1;
	for(int i=1;i<=z.l;i++)
	{
		z.d[i]=x.d[i]+y.d[i]+carry;
		carry=z.d[i]/10;
		z.d[i]%=10;
	}
	while(!z.d[z.l]) z.l--;
	return z;
}
supernumber com(supernumber x,supernumber y)
{
	if(x.l>y.l) return y;
	if(x.l<y.l) return x;
	if(x.l==y.l)
	{
		int p=x.l;
		while(x.d[p]==y.d[p]&&p)
			p--;
		if(!p) return x;
		if(x.d[p]>y.d[p]) return y;
		if(x.d[p]<y.d[p]) return x;
	}
}
int main()
{
	freopen("girls.in","r",stdin);
	freopen("girls.out","w",stdout);
	cin>>n>>total;
	for(int i=1;i<=n;i++)
		cin>>base[i]>>Time[i];
	for(int i=0;i<=n;i++)
		for(int j=0;j<=total;j++)
			f[i][j].d[200]=1,f[i][j].l=200;
	f[0][0].d[200]=f[0][0].l=0;
	for(int i=1;i<=n;i++) 
		for(int j=0;j<=total;j++)
		{
			memset(point.d,0,sizeof(point.d));
			point.d[1]=point.l=1;
			for(int k=1;k<=total;k++)
				point=mul(base[i],point);
			f[i][j]=add(f[i-1][j],point);
			if(j>=Time[i])
			{
				memset(point.d,0,sizeof(point.d));
				point.d[1]=point.l=1;
				for(int k=1;k<=j-Time[i];k++)
					point=mul(base[i],point);
				f[i][j]=com(f[i][j],add(f[i-1][j-Time[i]],point));
			}
        }
    answer.d[200]=1;
	answer.l=200;
    for(int i=1;i<=total;i++)
    	answer=com(answer,f[n][i]);
    for(int i=answer.l;i>=1;i--)
    	cout<<answer.d[i];
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值