USACO月赛题解 第三十讲 动态规划(一)

汇总见http://blog.csdn.net/qyl916/article/details/12442283

这五个DP题还是很不错的


第6题,方形牛棚 bigbrn

很经典的题目了,没有看题解,自己想了个解法

首先m较小的话可以参考另一篇博客,把点看成边界来做

这道题DP比较好一些

d[i][j]=min(d[i-1][j-1]+1,min( lft[i][j],up[i][j] ) );

lft和up分别表示左方和上方的空间,都能在平方级时间算出

d[i][j]表示以(i,j)为右下角顶点的边长最大值

因此整个算法是平方级的

void setio(string name){
	string in_f=name+".in";
	string out_f=name+".out";
	freopen(in_f.c_str(),"r",stdin);
	freopen(out_f.c_str(),"w",stdout);
}
const int maxn=1010;
int d[maxn][maxn],lft[maxn][maxn],up[maxn][maxn],a[maxn][maxn],n,m;
int main()
{
    setio("bigbrn");
	CLR(a,0);
	read2(n,m);
	REP(i,m){
        int x,y;
        read2(x,y);
        a[x][y]=1;
    }
    CLR(lft,0);CLR(up,0);
    REP1(i,n)lft[i][1]=a[i][1]==1?-1:0;
    REP1(i,n)FOR(j,2,n){
        lft[i][j]=a[i][j]==1?-1:lft[i][j-1]+1;
    }
    REP1(j,n)lft[1][j]=a[1][j]==1?-1:0;
    FOR(i,2,n)REP1(j,n){
        up[i][j]=a[i][j]==1?-1:up[i-1][j]+1;
    }
    CLR(d,0);
    int ans=0;
    REP1(i,n)REP1(j,n){
        d[i][j]=min(d[i-1][j-1]+1,min( lft[i][j],up[i][j] ) );
        ans=max(ans,d[i][j]);
    }
    /*REP1(i,n){REP1(j,n)write1(lft[i][j]);PN;}PN;
    REP1(i,n){REP1(j,n)write1(up[i][j]);PN;}PN;
    REP1(i,n){REP1(j,n)write1(d[i][j]);PN;}*/
    writeln(ans+1);
    //system("pause");
    return 0;
}



第8题 滑雪比赛 bobsled

这题明显是贪心,分类到DP应该是因为用到递推的关系。两个转弯点之间必然是这样的策略:不停加速,直到另一个转弯点“刹不住”,这样就能列出不等式,解出这个区间的最大速度,同时更新下一个转弯点的速度(因为可能不停加速不用刹车).这样就能递推+计算答案了。

但这样做有漏洞:假如两个转弯点之间的距离为1,前一个限速10,后一个限速1,显然第一个点速度只能到2.所以要计算一个弯角的“真限速”,计算方法不难,枚举这个点后面的所有弯角,将这些弯角的限速加上两个弯角之间的距离就行了。什么?这样是平方级的?平方算法有大量重复计算,完全可以变成线性的。


/*
40分 没考虑刹不住的问题
120分 三个小数据不对
仔细一看,终点速度没有算
还是错一个点 
大数据出错,INF太小 
*/ 

const LL INF = (LL)1<<50; 
struct node{
    LL t,s;
};
int cmp(node a, node b){
    return a.t < b.t;
}
void setio(string name){
    string in_f = name + ".in";
    string out_f = name + ".out";
    freopen(in_f.c_str(), "r", stdin);
    freopen(out_f.c_str(), "w", stdout);
}
node a[100010];
LL d[100010],m[100010],n,l;
int main()
{
    setio("bobsled");
	scanf("%lld%lld",&l,&n);
	REP1(i,n) scanf("%lld%lld",&a[i].t,&a[i].s);
	sort(a + 1, a + n + 1, cmp);
	a[0].t = 0; a[0].s = 1;
	a[n + 1].t = l; a[n + 1].s = INF;
	m[n + 1] = INF;
	for(LL i = n; i >= 0; i--) m[i]=min(m[i + 1], a[i].s + a[i].t);
	LL ans = 0;
	d[0]=1;
	REP1(i,n){
        d[i] = min(min(a[i].s, d[i-1] + a[i].t - a[i - 1].t), m[i + 1] - a[i].t);
        ans = max(ans, (a[i].t - a[i - 1].t + d[i] - d[i - 1] ) / 2 + d[i - 1] );
    }
    ans = max(ans, d[n] + l - a[n].t);
    cout<<ans<<endl;
    //while(1);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值