UVALive 3679 Pitcher Rotation(DP)
2017-03-17 16:36
309 查看
题目链接:点击打开链接
由于每个打手打完之后需要休息四天,从朴素的dp思路出发,我们只需要记录前四天选哪些打手就行了。
设dp[i][s1][s2][s3][s4]为从第1天到第i天,其中第i天选s4号打手,第i-1天选s3号打手,第i-2天选s2号打手,第i-3天选s1号打手的情况下的期望值。
状态转移方程为dp[i][s1][s2][s3][s4]=max(dp[i-1][s][s1][s2][s3]+p[i][s4]),其中s为枚举的第i-4天选的打手,在dp的时候需要保证s,s1,s2,s3,s4互不相同。
然而打手数最多会到100,如果这么设dp的话很显然时间和空间都会爆。
往贪心的方向想,假设打手不需要休息的话,那么每次都选胜率最高的打手出场就行了。
再假设打手需要休息一天,那么每次只需要从胜率最高的和胜率次高的两个选手中选一个就行了。
按照这个思想推下来,如果打手需要休息四天的话,那么每次比赛只需要从当前胜率最高的五个人中选一个就行了。
于是我们事先记录好当天每个人的胜率,然后降序排序,每次只需要从前五个中挑就行了。
于是s1,s2,s3,s4也可以改为表示选了某一天的第几高胜率的打手,状态转移也稍加修改就可以了。
对于不需要打比赛的时候,为了统一处理我们可以假设这个时候也需要打比赛。但是这些“比赛”又不能由题目给出的打手打。
对此,我们可以另外“创造”出五个打手,这些打手在所有比赛的胜率均为0(包括休息的时候),而我们假设题目给出的打手在不需要打比赛的时候的胜率为负无穷。
于是在dp过程中遇到不需要打比赛的时候这五个额外加进去的“打手”就会被用来填补空缺。
按照这样进行dp,最终取dp[g+10][s1][s2][s3][s4]中的最大值再除以100即可。
代码如下:
#include<bits/stdc++.h>
using namespace std;
struct node
{
int i,w;
node(int ii=0,int ww=0):i(ii),w(ww){}
bool operator <(const node &a)const
{
return a.w<w;
}
};
int n,m,g;
vector<node> v[115];
int d[225];
int dp[225][7][7][7][7];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&m,&g);
g+=10;
for(int i=0;i<=m;i++)
v[i].clear();
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
int temp;
scanf("%d",&temp);
v[i].push_back(node(j,temp));
}
for(int j=1;j<=5;j++)
v[i].push_back(node(n+j,0));
sort(v[i].begin(),v[i].end());
}
for(int i=1;i<=5;i++)
v[0].push_back(node(n+i,0));
for(int j=1;j<=n;j++)
v[0].push_back(node(j,-1000000));
for(int i=1;i<=g;i++)
scanf("%d",&d[i]);
memset(dp,-1,sizeof(dp));
for(int s1=0;s1<5;s1++)
for(int s2=0;s2<5;s2++)
for(int s3=0;s3<5;s3++)
for(int s4=0;s4<5;s4++)
dp[0][s1][s2][s3][s4]=0;
for(int i=1;i<=g;i++)
for(int s1=0;s1<5;s1++)
for(int s2=0;s2<5;s2++)
{
if(i>3&&v[d[i-3]][s1].i==v[d[i-2]][s2].i)continue;
for(int s3=0;s3<5;s3++)
{
if(i>3&&v[d[i-3]][s1].i==v[d[i-1]][s3].i)continue;
if(i>2&&v[d[i-2]][s2].i==v[d[i-1]][s3].i)continue;
for(int s4=0;s4<5;s4++)
{
if(i>3&&v[d[i-3]][s1].i==v[d[i]][s4].i)continue;
if(i>2&&v[d[i-2]][s2].i==v[d[i]][s4].i)continue;
if(i>1&&v[d[i-1]][s3].i==v[d[i]][s4].i)continue;
for(int s=0;s<5;s++)
{
if(i>4&&v[d[i-3]][s1].i==v[d[i-4]][s].i)continue;
if(i>4&&v[d[i-2]][s2].i==v[d[i-4]][s].i)continue;
if(i>4&&v[d[i-1]][s3].i==v[d[i-4]][s].i)continue;
if(i>4&&v[d[i]][s4].i==v[d[i-4]][s].i)continue;
if(dp[i-1][s][s1][s2][s3]==-1)continue;
dp[i][s1][s2][s3][s4]=max(dp[i][s1][s2][s3][s4],dp[i-1][s][s1][s2][s3]+v[d[i]][s4].w);
}
// printf("dp[%d][%d][%d][%d][%d]=%d\n",i,s1,s2,s3,s4,dp[i][s1][s2][s3][s4]);
}
}
}
int ans=-1;
for(int s1=0;s1<5;s1++)
for(int s2=0;s2<5;s2++)
for(int s3=0;s3<5;s3++)
for(int s4=0;s4<5;s4++)
ans=max(ans,dp[g][s1][s2][s3][s4]);
printf("%.2lf\n",(double)(ans)/100);
}
return 0;
}
由于每个打手打完之后需要休息四天,从朴素的dp思路出发,我们只需要记录前四天选哪些打手就行了。
设dp[i][s1][s2][s3][s4]为从第1天到第i天,其中第i天选s4号打手,第i-1天选s3号打手,第i-2天选s2号打手,第i-3天选s1号打手的情况下的期望值。
状态转移方程为dp[i][s1][s2][s3][s4]=max(dp[i-1][s][s1][s2][s3]+p[i][s4]),其中s为枚举的第i-4天选的打手,在dp的时候需要保证s,s1,s2,s3,s4互不相同。
然而打手数最多会到100,如果这么设dp的话很显然时间和空间都会爆。
往贪心的方向想,假设打手不需要休息的话,那么每次都选胜率最高的打手出场就行了。
再假设打手需要休息一天,那么每次只需要从胜率最高的和胜率次高的两个选手中选一个就行了。
按照这个思想推下来,如果打手需要休息四天的话,那么每次比赛只需要从当前胜率最高的五个人中选一个就行了。
于是我们事先记录好当天每个人的胜率,然后降序排序,每次只需要从前五个中挑就行了。
于是s1,s2,s3,s4也可以改为表示选了某一天的第几高胜率的打手,状态转移也稍加修改就可以了。
对于不需要打比赛的时候,为了统一处理我们可以假设这个时候也需要打比赛。但是这些“比赛”又不能由题目给出的打手打。
对此,我们可以另外“创造”出五个打手,这些打手在所有比赛的胜率均为0(包括休息的时候),而我们假设题目给出的打手在不需要打比赛的时候的胜率为负无穷。
于是在dp过程中遇到不需要打比赛的时候这五个额外加进去的“打手”就会被用来填补空缺。
按照这样进行dp,最终取dp[g+10][s1][s2][s3][s4]中的最大值再除以100即可。
代码如下:
#include<bits/stdc++.h>
using namespace std;
struct node
{
int i,w;
node(int ii=0,int ww=0):i(ii),w(ww){}
bool operator <(const node &a)const
{
return a.w<w;
}
};
int n,m,g;
vector<node> v[115];
int d[225];
int dp[225][7][7][7][7];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&m,&g);
g+=10;
for(int i=0;i<=m;i++)
v[i].clear();
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
int temp;
scanf("%d",&temp);
v[i].push_back(node(j,temp));
}
for(int j=1;j<=5;j++)
v[i].push_back(node(n+j,0));
sort(v[i].begin(),v[i].end());
}
for(int i=1;i<=5;i++)
v[0].push_back(node(n+i,0));
for(int j=1;j<=n;j++)
v[0].push_back(node(j,-1000000));
for(int i=1;i<=g;i++)
scanf("%d",&d[i]);
memset(dp,-1,sizeof(dp));
for(int s1=0;s1<5;s1++)
for(int s2=0;s2<5;s2++)
for(int s3=0;s3<5;s3++)
for(int s4=0;s4<5;s4++)
dp[0][s1][s2][s3][s4]=0;
for(int i=1;i<=g;i++)
for(int s1=0;s1<5;s1++)
for(int s2=0;s2<5;s2++)
{
if(i>3&&v[d[i-3]][s1].i==v[d[i-2]][s2].i)continue;
for(int s3=0;s3<5;s3++)
{
if(i>3&&v[d[i-3]][s1].i==v[d[i-1]][s3].i)continue;
if(i>2&&v[d[i-2]][s2].i==v[d[i-1]][s3].i)continue;
for(int s4=0;s4<5;s4++)
{
if(i>3&&v[d[i-3]][s1].i==v[d[i]][s4].i)continue;
if(i>2&&v[d[i-2]][s2].i==v[d[i]][s4].i)continue;
if(i>1&&v[d[i-1]][s3].i==v[d[i]][s4].i)continue;
for(int s=0;s<5;s++)
{
if(i>4&&v[d[i-3]][s1].i==v[d[i-4]][s].i)continue;
if(i>4&&v[d[i-2]][s2].i==v[d[i-4]][s].i)continue;
if(i>4&&v[d[i-1]][s3].i==v[d[i-4]][s].i)continue;
if(i>4&&v[d[i]][s4].i==v[d[i-4]][s].i)continue;
if(dp[i-1][s][s1][s2][s3]==-1)continue;
dp[i][s1][s2][s3][s4]=max(dp[i][s1][s2][s3][s4],dp[i-1][s][s1][s2][s3]+v[d[i]][s4].w);
}
// printf("dp[%d][%d][%d][%d][%d]=%d\n",i,s1,s2,s3,s4,dp[i][s1][s2][s3][s4]);
}
}
}
int ans=-1;
for(int s1=0;s1<5;s1++)
for(int s2=0;s2<5;s2++)
for(int s3=0;s3<5;s3++)
for(int s4=0;s4<5;s4++)
ans=max(ans,dp[g][s1][s2][s3][s4]);
printf("%.2lf\n",(double)(ans)/100);
}
return 0;
}
相关文章推荐
- UVALive 7276 Wooden Signs (DP)
- UVALive 4731 Cellular Network(贪心,dp)
- uvalive7271(A Math Problem) 数位dp
- UVALive 2031 Dance Dance Revolution (舞步转移,状态压缩DP,4级)
- UVALive 6661 - Equal Sum Sets (类似硬币的DP问题)
- 【UVALive 7505】Hungry Game of Ants(DP)
- UVALive 7271 A Math Problem 【数位dp计数】
- UVALive - 4794 Sharing Chocolate DP
- UVALive 6042 Bee Tower (dp)
- UvaLive 4256 Salesman (DP)
- UVALive 6434 Number Assignment(dp)
- [UVALive3675]Sorted bit sequence && 数位DP
- UVaLive LA 4256 | UVa 1424 - Salesmen(简单DP)
- UVaLive 4327 | POJ 3926 - Parade (单调队列优化DP)
- UVaLive 4256 Salesmen (简单DP)
- uvalive3637(DP)
- UVALive 6430 Points(dp)
- UVALive 5790 Ball Stacking DP
- UVALIVE 3346 Perfect Domination on Trees 树形DP
- uva live4731 蜂窝网络 题解(dp+贪心)