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记录下每一条斜线上的最大值,然后直接转移过来就行了。
/* *\
* 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;
}