原题地址 具体的题就不贴了
D.Links
明显的dp,找递推式就行了。
所以我一开始直接下手写一般情况,写完一般情况,发现 欸? i+k 可能超出了 n,行,那就加条if语句判断一下,分段讨论。
写完分段,又发现 欸 ? a[i]+k 也可能 超出了 n, 行, 我再改, 然后....可能k+1直接 > n。 再改!
没问题了,直接提交, WA.....
以上是我的wa题经验,不要学习。
所以我想说的是,能让计算机做的判断就不要自己去做。这个分段函数的处理,直接让计算机处理好了,你给个通项公式,多好,不用想半天结果还是wa,也找不出错误。
正确解法:
初始化dp[0].
递推式的通项:dp[i] = dp[i-1] + num - repeat;
其中,dp[i] 是从第i个开始读消息最多能读到消息数。
num 是第i条消息和它左右两边可见消息数,num= min(n, i+k)-max(1, i-k)+1;
repeat = max(0,min(n,a[i]+k) - max( 1,i - k) + 1);
这样基本ok了。但是要留意的是当a[i] = 0时,repeat =0;
就这样
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[100009];
int dp[100009];
int main()
{
int n,k;
scanf("%d%d",&n,&k);
a[0] =0;
for(int i=1;i<=n;i++)
{
scanf("%d",a+i);
}
//memset(dp,0,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;i++)
{
int num = min(n,i+k) - max(1,i-k) + 1;
int repeat = max(0,min(n,a[i]+k) - max( 1,i - k) + 1);
if(a[i]==0)
dp[i] = num;
else
dp[i] = dp[a[i]] + num - repeat;
}
for(int i=1;i<=n;i++)
printf("%d ",dp[i]);
printf("\n");
system("pause");
return 0;
}
G.Bounty Hunters
地推公式:
dp(i,S)表示当前在点i处,还需要访问S中的所有点并回到起点,所需要的距离
dp[i][S] = min{dp[j][S-j] + dis[j]};
解体思路并不难,难的是集合表示,和枚举k元子集。
这里我用的是二进制表示集合,1代表有,0代表没有。
枚举子集据说《挑战》上有反正我没看==,下次还是要买这本书。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
char a[20][20];
int dp[16][1<<16],posi[16],posj[16],dis[16][16];
int main()
{
int t;
scanf("%d",&t);
for(int tt=0;tt<t;tt++)
{
int m,n,gcnt=1;
scanf("%d%d",&m,&n);
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
// getchar();
// scanf("%c",a[i]+j);
cin>>a[i][j];
if(a[i][j]=='g')
{
posi[gcnt] = i;
posj[gcnt] =j ;
gcnt++;
}
if(a[i][j]== 'x')
{
posi[0] = i;
posj[0] = j;
}
}
}
for(int i=0; i<gcnt;i++)
for(int j=0;j<gcnt;j++)
{
int x=abs(posi[i]-posi[j]),y=abs(posj[i]-posj[j]);
dis[i][j]= max(x,y);
}
memset(dp,0x3f,sizeof(dp));
for(int i=0;i<gcnt;i++)
{
dp[i][0] = dis[0][i];
}
for(int k=1;k<gcnt;k++)//k元
{
int s=(1<<k) - 1;//最小开始
while(s < (1<<gcnt))
{
for(int i=0;i<gcnt;i++)//对当前位置枚举
{
if(!((s>>i) & 1))//i不在集合s
for(int j=0;j<gcnt;j++)
{
if((s>>j) & 1)//j在集合s
dp[i][s]=min(dp[i][s],dp[j][s-(1<<j)]+dis[i][j]);
}
}
int low1=s & (-s);//找下一个k元子集
int ss=s + low1;
s= s & (~ss);
s = ss | ((s/low1)>>1);
}
}
printf("Case %d: %d\n",tt+1,dp[0][(1<<gcnt)-2]);
}
system("pause");
return 0;
}
就这样吧,写的很不详细,估计也帮不了谁,毕竟想介绍dp的好文章那么多,看那些足够了。我这就给自己加深印象而已。