对于这套题目,用了两天,艰难的写了5个题,有的题目感觉有点坑啊!
其中A题 Assignment For Princess 用构造的方法去做,可是很难去证明点什么。
D题 Dinner Coming Soon 题意真心好纠结,在我乱wa10次以后终于搞定题意,AC掉!当然,是由于个人原因,已经进入提交暴躁状态。。。
F题 Fibonacci Tree 读完题目以后的第一想法是先构造最小生成树,然后可以通过调整去想办法yy出来一棵Fibonacci Tree? 事实就是我想多了。。。
H题Hard Disk Drive,大水题一枚,不多说了;
J题 Just Random 先TLE,优化以后就AC了。
下面具体讲一些我做的过程中遇到的一些细节问题。
A题 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4781
题意:将n个点用m条长度分别为1~m的有向边,构造出一个无自环的两点之间最多一条边的强连通图,且满足从任一点经过任意路径回到这一点的路径长度是3的倍数。(10 <= N <= 80, N+3 <= M <= N2/7 )
分析求解:题目读过以后就觉得无从下手,数据给的也很奇怪,n+3还可以理解,是为了按顺序构造出一个满足整除3的环,M <= N2/7就有点不知道为什么了,后来我分析,出题的人大概是为了满足答案可解吧!那这个题构造就很好想了,一是先按点的顺序连成环,二是根据第一步连成的环求出两点之间的距离d[i][j],将剩下的边插放到满足要求的地方就可以了,边v只要满足v%3==d[i][j]%3就可以连在i,j之间。
D题 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4784
题意:题意很纠结,大家自己来读吧!题意读懂了,dp还是比较好做的。目测这个dp78ms是现在hdu上最快的了吧,听说很多喜欢写spfa的都T掉了,加上优化应该也是可以AC的。
分析求解:我做的时候最初没有高清楚的地方:1.有向边,傻傻的做成了无向边;
2.从1出发,中途可以回到1;但是一旦到达n点,就结束了;
3.在1和n点不可以交易;
4.任何时刻 任何地点不能出现钱为负数;
当然,还要注意1和n点是没有除了0世界的其他世界。
那么,状态dp[t][d][p]可以表示时间t在d号点有p包盐的状态下最多有的money,[d]这一维也可以写成二维的[k][dd],表示k维世界的dd号点,d=k*n+dd,我个人比较喜欢压在一维来写。
然后根据时间推移来转移状态,对于某时间某点,可以从当前平行世界的点转移过来,还可以从该点的上一个世界过来(除1,n),转移的过程中还可以选择买盐,卖盐,或者不买不卖三种。
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
int n,m,b,k,r,t;
int pr[10][110];//盐的价格;
int dp[210][600][10];
int head[200];
struct edge
{
int v,t,m,next;
}e[1000];
int tot;
void add(int u,int v,int t,int m)
{
tot++;
e[tot].v=v;
e[tot].t=t;
e[tot].m=m;
e[tot].next=head[u];
head[u]=tot;
}
void find(int tt,int d)
{
int dd=d%n;
if (d==n) dd=n;
int ceng=d/n;
if (d==n) ceng=0;
for (int i=head[dd];i!=-1;i=e[i].next)
if (e[i].v!=n)//不能从n点来到当前d点;
if (!(e[i].v==1&&ceng>0))//不能从1号点且非0号世界的点来到当前d点;
{
int come=ceng*n+e[i].v;
if (tt-e[i].t>=0)
{
for (int j=0;j<=b;j++)
{
if (dp[tt-e[i].t][come][j]!=-1&&dp[tt-e[i].t][come][j]-e[i].m>=0&&
dp[tt][d][j]<dp[tt-e[i].t][come][j]-e[i].m)
dp[tt][d][j]=dp[tt-e[i].t][come][j]-e[i].m;//无交易;
if (d!=n&&d!=1&&j>=1&&dp[tt-e[i].t][come][j-1]!=-1&&
dp[tt-e[i].t][come][j-1]-e[i].m-pr[ceng][dd]>=0&&
dp[tt][d][j]<dp[tt-e[i].t][come][j-1]-e[i].m-pr[ceng][dd])
dp[tt][d][j]=dp[tt-e[i].t][come][j-1]-e[i].m-pr[ceng][dd];//买盐;
if (d!=n&&d!=1&&j<b&&dp[tt-e[i].t][come][j+1]!=-1&&
dp[tt-e[i].t][come][j+1]-e[i].m>=0&&
dp[tt][d][j]<dp[tt-e[i].t][come][j+1]-e[i].m+pr[ceng][dd])
dp[tt][d][j]=dp[tt-e[i].t][come][j+1]-e[i].m+pr[ceng][dd];//卖盐;
}
}
}
if (d!=n&&d!=1)
{
int come;
if (ceng==0)
come=(k-1)*n+dd;
else come=d-n;
for (int j=0;j<=b;j++)
{
if (dp[tt-1][come][j]!=-1&&dp[tt-1][come][j]>=0&&
dp[tt][d][j]<dp[tt-1][come][j])
dp[tt][d][j]=dp[tt-1][come][j];//无交易;
if (j>=1&&dp[tt-1][come][j-1]!=-1&&dp[tt-1][come][j-1]-pr[ceng][dd]>=0&&
dp[tt][d][j]<dp[tt-1][come][j-1]-pr[ceng][dd])
dp[tt][d][j]=dp[tt-1][come][j-1]-pr[ceng][dd];//买盐;
if (j<b&&dp[tt-1][come][j+1]!=-1&&dp[tt-1][come][j+1]>=0&&
dp[tt][d][j]<dp[tt-1][come][j+1]+pr[ceng][dd])
dp[tt][d][j]=dp[tt-1][come][j+1]+pr[ceng][dd];//卖盐;
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
int T,cas=0;
scanf("%d",&T);
while (T--)
{
cas++;
printf("Case #%d: ",cas);
scanf("%d%d%d%d%d%d",&n,&m,&b,&k,&r,&t);
for (int i=0;i<k;i++)
for (int j=1;j<=n;j++)
scanf("%d",&pr[i][j]);
memset(head,-1,sizeof(head));
tot=0;
for (int i=1;i<=m;i++)
{
int u,v,tt,mm;
scanf("%d%d%d%d",&u,&v,&tt,&mm);
add(v,u,tt,mm);//我dp是从后往前找的,所以加的是反向边;
}
memset(dp,-1,sizeof(dp));//初值,-1表示无解;
dp[0][1][0]=r;
for (int tt=1;tt<=t;tt++)
{
for (int i=0;i<k;i++)
for (int j=2;j<n;j++)
{
int d=i*n+j;
find(tt,d);//更新时间tt,d点的解;
}
find(tt,1);
find(tt,n);
}
int ans=-1;
for (int i=0;i<=t;i++)
if (dp[i][n][0]>ans)
ans=dp[i][n][0];
if (ans==-1) printf("Forever Alone\n");
else printf("%d\n",ans);
}
return 0;
}
F题 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4786
题意:边权为0,1的无向图,构造一棵Fibonacci Tree,即树的边权值的总和是一个Fibonacci数;
分析求解:找最小生成树和最大生成树之间有没有Fibonacci数;验证最小生成树和最大生成树之间的所有权值都是可构造的这一点我还没有特别严格的证明。
H题和J题比较水,J题就是注意不要TLE!