【动态规划】贝茜的晨练计划

2、贝茜的晨练计划(cowrun)

 

【题目描述】

    奶牛们打算通过锻炼来培养自己的运动细胞,作为其中的一员,贝茜选择的运动方式是每天进行N(1 <= N <= 10,000)分钟的晨跑。在每分钟的开始,贝茜会选择下一分钟是用来跑步还是休息。

    贝茜的体力限制了她跑步的距离。更具体地,如果贝茜选择在第i分钟内跑步,她可以在这一分钟内跑D_i(1 <= D_i<= 1,000)米,并且她的疲劳度会增加1。不过,无论何时贝茜的疲劳度都不能超过M(1 <= M <=500)。如果贝茜选择休息,那么她的疲劳度就会每分钟减少1,但她必须休息到疲劳度恢复到0为止。在疲劳度为0时休息的话,疲劳度不会再变动。晨跑开始时,贝茜的疲劳度为0。

    还有,在N分钟的锻炼结束时,贝茜的疲劳度也必须恢复到0,否则她将没有足够的精力来对付这一整天中剩下的事情。

   请你计算一下,贝茜最多能跑多少米。

 

【输入格式】

第1行: 2个用空格隔开的整数:N 和 M

第2..N+1行: 第i+1为1个整数:D_i

 

【输入样例】(cowrun.in)

5 2

5

3

4

2

10

 

【输出格式】

第1行: 输出1个整数,表示在满足所有限制条件的情况下,贝茜能跑的最大距离

 

【输出样例】(cowrun.out)

9

 

【输出说明】

    贝茜在第1分钟内选择跑步(跑了5米),在第2分钟内休息,在第3分钟内跑步(跑了4米),剩余的时间都用来休息。因为在晨跑结束时贝茜的疲劳度必须为0,所以她不能在第5分钟内选择跑步。



我的方法是O(n^2*m)的时间复杂度,但是可以优化到O(n*m),此外还有直接O(n*m)的方程的方法。


朴素方程是

F[i,j] = F[i-1,j-1]+D[i]

F[i,0] = max(F[j,i-j],F[j,0])

第一个转移是O(1)的

第二个转移是O(n)的,但是我们观察发现,F[j,i-j]表示的是一条斜率为1的直线,且是当前枚举到的直线的前一条。因此我们可以用hash记录下每一条斜线上的最大值,然后直接转移过来就行了。


其实笔者就是个傻吊,有没有越看越觉得“朴素”方程是O(n*m)的?我还优化个毛啊。。虽然确实快点。。


/*					*\
 * 	By
 * 		Wu yihao
 * 					*/

#include <cstdio>
#include <cstring>
#include <string>

long into[120];
long out[120];
long fa[120];
bool used[120];

long K;

struct node
{
	long ind;
	node* nxt;
};
node* head[120];

void insert(long u,long v)
{
	node* nn = new node;
	nn -> ind = v;
	nn -> nxt = head[u];
	head[u] = nn;
}

long getroot(long u)
{
	if (fa[u] == u)
		return u;
	return fa[u]=getroot(fa[u]);
}

inline void merge(long u,long v)
{
	fa[getroot(u)] = getroot(v);
}

long getint()
{
	long rs=0;bool sgn=1;char tmp;
	do tmp = getchar();
	while (!isdigit(tmp)&&tmp!='-');
	if (tmp=='-'){tmp=getchar();sgn=0;}
	do rs=(rs<<3)+(rs<<1)+tmp-'0';
	while (isdigit(tmp=getchar()));
	return sgn?rs:-rs;
}

void dfs(long u)
{
	for (node* vv=head[u];vv;vv=vv->nxt)
	{
		if (vv->ind != K)
		{
			used[vv->ind] = true;
			dfs(vv->ind);
		}
	}
}

int main()
{
	freopen("contest.in","r",stdin);
	freopen("contest.out","w",stdout);

	long n = getint();
	long m = getint();

	for (long i=1;i<n+1;i++)
		fa[i] = i;

	for (long i=1;i<m+1;i++)
	{
		long u = getint();
		long v = getint();
		into[v] ++;
		out[u] ++;
		insert(u,v);
		merge(u,v);
	}

	for (long i=2;i<n+1;i++)
	{
		if (getroot(i) != getroot(i-1))
		{
			printf("0");
			return 0;
		}
	}

	long ans = 0;

	for (long i=1;i<n+1;i++)
	{
		memset(used,0,sizeof used);
		for (long j=1;j<n+1;j++)
		{
			if (into[j] == 0 && j != i)
			{
				used[j] = true;
				K = i;
				dfs(j);
			}
		}
		bool ok = true;
		for (long j=1;j<n+1;j++)
		{
			if (out[j] == 0 && used[j])
			{
				ok = false;
				break;
			}
		}

		if (ok)
		{
			ans ++;
		}
	}

	printf("%ld",ans);
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值