pku2904: http://poj.org/problem?id=2904
题意:给k个油桶,要试验其能承受的爆炸底线,求对于最坏的情况(爆炸底线m最大)时需要试验的最少次数,比如k=1,m=10,则需从1开始试验,直到油桶爆炸,便可知起爆炸底线为该值-1,若为最坏情况,即为10,则要试验1+2+3……+10。若有2个油桶,可任意取一个值i试验,若油桶没有爆炸,则说明底线>i,否则<i且只剩一个油桶,需要从1开始试验(否则再炸了就没有油桶可以试验了)
解法:dp:dp[i][k][j]表示用第i个油桶从k到j试验的最优值,方程为dp[i][k][j]=min(dp[i][k][j],max(dp[i-1][k][t-1]+t,dp[i][t+1][j]+t))(k<=t<=j),表示如果油桶爆炸,则i-1,爆炸底线<t,所以k~t-1;如果不爆炸,则i,爆炸底线>t,所以t+1~j,(t已经确定不是了,所以不是t~j),因为是最坏情况,所以取最坏路径即max。
code:
#include<iostream>
#include<cstdio>
#include<cstdlib>
const int inf=1<<29;
int dp[20][150][150];
int min(int a,int b)
{
if(a<b)
return a;
else
return b;
}
int max(int a,int b)
{
if(a>b)
return a;
else
return b;
}
int main()
{
int n,k,m;
memset(dp,0,sizeof(dp));
for(int i=1;i<=100;i++)
{
for(int j=i;j<=100;j++)
{
dp[1][i][j]=(j-i+1)*(i+j)/2;
}
}
for(int i=2;i<=10;i++)
{
for(int j=1;j<=100;j++)
{
for(int k=j;k>0;k--) //倒序枚举
{
dp[i][k][j]=inf;
for(int t=k;t<=j;t++)
{
dp[i][k][j]=min(dp[i][k][j],max(dp[i-1][k][t-1]+t,dp[i][t+1][j]+t)); //每个状态由从i-1(已算过)状态和[i][t+1][j](后面的状态)得到的,所以应先算后面的状态,即前面k倒序
}
}
}
}
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&k,&m);
printf("%d\n",dp[k][1][m]);
}
}
/*input:
4
1 10
1 100
3 73
5 100
output:
55
5050
382
495
*/