好吧,这名字真是让我想起了某段被某教科书支配的历史.....各种DP题层出不穷,不过终于做完了orz

虽然各种手糊加乱搞,但还是要总结一下.

T1 Monkey Banana Problem 

 

这道题和数塔问题差不多,不过多了一个倒过来的

代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std; long long a[][],t,n; int main()
{
int ttt=;
scanf("%lld",&t);
while(t--)
{
ttt++;
memset(a,,sizeof(a));
scanf("%lld",&n);
for(int i=; i<=n; i++)
{
for(int j=; j<=i; j++)
{
scanf("%lld",&a[i][j]);
}
}
for(int i=n+; i<=*n-; i++)
{
for(int j=; j<=*n-i; j++)
{
scanf("%lld",&a[i][j]);
}
}
for(int i=; i<=n; i++)
{
for (int j=; j<=i; j++)
{
a[i][j]+=max(a[i-][j-],a[i-][j]);
}
}
for(int i=n+; i<=*n-; i++)
{
for(int j=; j<=*n-i; j++)
{
a[i][j]+=max(a[i-][j],a[i-][j+]);
}
}
printf("Case %d: %lld\n",ttt,a[*n-][]);
}
}

T2Rooks

T3Marriage Ceremonies

 

 

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 17 int dp[N][<<N];
int a[N][N];
int t,n,ans; int main()
{
int ttt=;
scanf("%d",&t);
while(t--)
{
ttt++;
memset(dp,,sizeof(dp));
memset(a,,sizeof(a));
scanf("%d",&n);
for(int i=; i<n; i++)
{
for(int j=; j<n; j++)
{
scanf("%d",&a[i][j]);
}
}
for(int i=; i<n; i++)
{
dp[][(<<i)]=a[][i];
}
for(int i=; i<n; i++)
{
for(int sta=; sta<=(<<n)-; sta++)
{
if(!dp[i-][sta])
{
continue;
}
for(int j=; j<n; j++)
{
if(sta&(<<j))
{
continue;
}
dp[i][sta|(<<j)]=max(dp[i][sta|(<<j)],dp[i-][sta]+a[i][j]);
}
}
}
printf("Case %d: %d\n",ttt,dp[n-][(<<n)-]);
}
}

T4Guilty Prince

代码:

#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std; char maze[][];
bool vis[][];
int t,n,m,ans,x,y; int bfs(int x,int y)
{
queue<pair<int,int> > q;
q.push(make_pair(x,y));
vis[x][y]=;
while(!q.empty())
{
int nx=q.front().first;
int ny=q.front().second;
q.pop();
if(!vis[nx+][ny]&&nx+<=n&&nx+>=&&ny<=m&&ny>=)
{
q.push(make_pair(nx+,ny));
ans++;
vis[nx+][ny]=;
}
if(!vis[nx-][ny]&&nx-<=n&&nx->=&&ny<=m&&ny>=)
{
q.push(make_pair(nx-,ny));
ans++;
vis[nx-][ny]=;
}
if(!vis[nx][ny+]&&nx<=n&&nx>=&&ny+<=m&&ny+>=)
{
q.push(make_pair(nx,ny+));
ans++;
vis[nx][ny+]=;
}
if(!vis[nx][ny-]&&nx<=n&&nx>=&&ny-<=m&&ny->=)
{
q.push(make_pair(nx,ny-));
ans++;
vis[nx][ny-]=;
}
}
} int main()
{
int ttt=;
scanf("%d",&t);
while(t--)
{
ttt++;
ans=;
scanf("%d%d",&m,&n);
memset(vis,,sizeof(vis));
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
cin>>maze[i][j];
if(maze[i][j]=='@')
{
x=i;
y=j;
}
if(maze[i][j]=='#')
{
vis[i][j]=;
}
}
}
bfs(x,y);
printf("Case %d: %d\n",ttt,ans);
}
}

 T5 Brush (III)

 

 

给你一个刷子,刷子有宽度w,允许你平行x轴刷k次,告诉你有n个点,求刷k次能刷完的最多的点的个数.

好吧,最初就是想暴力乱搞,然后发现xi,yi都有10^9,就想到了离散化.离散化y的坐标,统计一个cnt[i]表示第i个y值上有几个点,然后再搞一个num[i]表示以y[i]为下界宽为w的区域中点的个数,不过因为cnt数组足以胜任这一点。

然后就可以开始dp了

对于每一个cnt[i],我们可以选择从这个点开始刷还是不刷,对于每个点来说往左刷一个单位还是往右刷一个单位显然是没有任何意义的,即我们还是得从以每个点开始dp

很容易推出dp的转移方程

dp[i][j]表示刷到第j个点以后已经刷了i次的最大值

那么dp[i][j]=max{dp[i][j],dp[i-1][( 1~( j-1))]+cnt[j]}

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 110 struct point
{
int x,y;
} poi[N];
int n,w,k,t,tmp,ans;
int cnt[N],y[N],dp[N][N]; bool cmp(point a,point b)
{
return a.y<b.y;
} void discretization_it()
{
sort(poi+,poi+n+,cmp);
tmp=;
y[tmp]=poi[].y;
cnt[tmp]=;
for(int i=; i<=n; i++)
{
if(poi[i].y==y[tmp])
{
cnt[tmp]++;
}
else
{
y[++tmp]=poi[i].y;
cnt[tmp]=;
}
}
for(int i=; i<=tmp; i++)
{
for(int j=i+; j<=tmp; j++)
{
if(y[j]-y[i]<=w)
{
cnt[i]+=cnt[j];
}
}
}
} int main()
{
int ttt=;
scanf("%d",&t);
while(t--)
{
ttt++;
ans=;
memset(dp,,sizeof(dp));
scanf("%d%d%d",&n,&w,&k);
for(int i=; i<=n; i++)
{
scanf("%d %d",&poi[i].x,&poi[i].y);
}
discretization_it();
for(int i=; i<=tmp; i++)
{
dp[][i]=cnt[i];
}
for(int i=; i<=k;i++)
{
for(int j=; j<=tmp; j++)
{
for(int l=; l<j; l++)
{
if (y[j]-y[l]<=w)
{
break;
}
if (y[j]-y[l]>w&&dp[i][j]<dp[i-][l]+cnt[j])
{
dp[i][j]=dp[i-][l]+cnt[j];
}
}
}
}
for(int i=;i<=k;i++)
{
for(int j=;j<=tmp;j++)
{
ans=max(ans,dp[i][j]);
}
}
printf("Case %d: %d\n",ttt,ans);
} }

 T6 Brush (IV)

这回的刷子升级了,可以向任意的方向刷,不过刷子没有宽度,即几个点共线才能被刷掉,求刷掉所有点的最小次数

因为点的个数的数据范围只有16,所以可以想到状态压缩动态规划,选取i,j两个点,无非是选择是否要连i,j然后如果要选的话就把和i,j三点共线的所有点都加到当前的状态里去,然后花费1的次数将之前状态转移到现在状态。假设a[i][j]为i,j所在直线上的点的个数,这里a[i][j]表示二进制位下在i,j直线上所有点的位置。

所以dp[sta|a[i][j]]=min{dp[sta|a[i][j],dp[sta]+1}

代码:

#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f struct point
{
int x,y;
}poi[];
int a[][];
int dp[(<<)];
int n,t;
vector <int> yyk[(<<)];
//int lowbit(int x)
//{
// return x&-x;
//} bool judge(point x,point y,point z)
{
int tmp=(y.x-x.x)*(z.y-x.y)-(y.y-x.y)*(z.x-x.x);
if(!tmp)
{
return ;
}
else
{
return ;
}
} void pre()
{
memset(a,,sizeof(a));
for(int i=;i<n;i++)
{
a[i][i]|=(<<i);
for(int j=i+;j<n;j++)
{
for(int k=;k<n;k++)
{
if(judge(poi[i],poi[j],poi[k]))
{
a[i][j]|=(<<k);
}
}
}
}
} void db()
{
for(int sta=;sta<=(<<);sta++)
{
for(int j=;j<;j++)
{
if((sta&<<j)==)
{
yyk[sta].push_back(j);
}
}
}
} int main()
{
int ttt=;
scanf("%d",&t);
db();
while(t--)
{
ttt++;
scanf("%d",&n);
for(int i=;i<=(<<n);i++)
{
dp[i]=inf;
}
for(int i=;i<n;i++)
{
scanf("%d %d",&poi[i].x,&poi[i].y);
}
pre();
dp[]=;
for(int sta=;sta<(<<n)-;sta++)
{
int x=yyk[sta][];
for(int j=;j<yyk[sta].size();j++)
{
int y=yyk[sta][j];
dp[sta|a[x][y]]=min(dp[sta|a[x][y]],dp[sta]+);
}
}
printf("Case %d: %d\n",ttt,dp[(<<n)-]);
}
return ;
}

T7 Painful Bases

给出一个值base,一个值k,一个base进制下的的数,求该数的全排列中模k余0的数的个数。

emmmm。。。这倒是比较老的套路了。。。首先看到求模数为几,自然是想到开一维来记录模数,这有点类似于数位dp中某些题的思路。而再乍一看这个给的数的长度不会超过16,自然是想到用状压去做。所以设置dp[sta][mod]表示当已经放好sta个数后这些数余数为mod的数的个数。

所以状态转移方程如下:dp[sta|(1<<j)][(mod*base+a[j]%k)%k]+=dp[sta][mod]

代码:

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std; long long dp[][(<<)];
char s[];
int t,base,k,a[]; int main()
{
int ttt=;
scanf("%d",&t);
while(t--)
{
ttt++;
memset(dp,,sizeof(dp));
scanf("%d%d",&base,&k);
cin>>s;
int len=strlen(s);
for(int i=;i<len;i++)
{
if(s[i]<=''&&s[i]>='')
{
a[i]=s[i]-'';
}
else
{
a[i]=s[i]-'A'+;
}
}
int up=<<len;
dp[][]=;
for(int sta=;sta<up;sta++)
{
for(int j=;j<len;j++)
{
if(sta&(<<j))
{
continue;
}
for(int x=;x<k;x++)
{
if(!dp[x][sta])
{
continue;
}
dp[(x*base+a[j])%k][sta|(<<j)]+=dp[x][sta];
}
}
}
printf("Case %d: %lld\n",ttt,dp[][up-]);
}
}

 

T8 the specials menu

给出一个字符串,求这个字符串中回文串的个数,你可以去掉一些字母。

噫,又是套路。。。区间dp老题了

用dp[i][j]表示区间i-j之间的方案数

则存在以下两种转移

1.s[i]=s[j]时:只留ij显然多了一种方案,dp[i][j]=dp[i+1][j]+dp[i][j-1]+1

2.s[i]!=s[j]时:显然存在重复dp[i][j]=dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]

代码:

 

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 110 long long dp[N][N],t,ttt; int main()
{
scanf("%lld",&t);
while(t--)
{
char s[N];
ttt++;
memset(dp,,sizeof(dp));
cin>>s+;
int len=strlen(s+);
for(int i=;i<=len;i++)
{
dp[i][i]=;
}
for(int i=len;i>=;i--)
{
for(int j=i+;j<=len;j++)
{
dp[i][j]+=dp[i+][j]+dp[i][j-]-dp[i+][j-];
if(s[i]==s[j])
{
dp[i][j]+=dp[i+][j-]+;
} }
}
printf("Case %lld: %lld\n",ttt,dp[][len]);
} }

 

 

 

T9 A Dangerous Maze

有n扇门,每扇门对应着一个值(xi),为正代表xi分后这个人能够离开,为负表示abs(xi)分后这个人会重新回到原地,并且这个人仍是1/n的概率重新再选一扇门。求他离开的期望?

好吧。。。感觉这道题又和dp没关系啊。。。

这是一道纯种的期望题。

首先对于门只有两种功能:要么把人传回去,要么送他出来,假设期望为E。当前耗时为C

这扇门是好门,将你传送出去,期望为C*1/n

这扇门是坏门,将你传送回来,期望为(E+C)*1/n

设总共有X扇坏门,好门消耗时间总和为sum1,坏门为sum2

则可以列出方程:

E=(sum1)/n+(E*X+sum2)/n

E=(sum1+sum2+E*X)/n

nE=sum1+sum2+E*X

(n-X)*E=sum1+sum2;

E=(sum1+sum2)/(n-X)

当然n-X为零的话他就永远出不去了

然而与dp并无关系的说。。。

代码:

 

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std; int t,n,a,sum,div1,nag,pos; int gcd(int a,int b)
{
if(a%b==)
{
return b;
}
return gcd(b,a%b);
} int main()
{
int ttt=;
scanf("%d",&t);
while(t--)
{
ttt++;
nag=;
sum=;
scanf("%d",&n);
for(int i=;i<=n;i++)
{
scanf("%d",&a);
if(a<)
{
nag++;
sum-=a;
}
else
{
sum+=a;
}
}
pos=n-nag;
if(pos==)
{
printf("Case %d: inf\n",ttt);
}
else
{
printf("Case %d: %d/%d\n",ttt,sum/gcd(pos,sum),pos/gcd(pos,sum));
} }
return ;
}

 T10 Discovering Gold

已知有一个宽为1,长为n的表格,表格由1*1的小正方形组成,每个小正方形中都有一个价值v[i],现在从起点1开始掷骰子,掷到几就向前走几步,并获得当前走到位置的值v,

如果投过n就重投。求获得价值的期望。

著名oi选手xxx曾说过“一切期望皆方程”

好的这又不是一道dp题(大雾)

总而言之,著名oi选手xxx说过“概率正推,期望倒推”

所以DP[I]=(DP[I+1]+DP[I+2]+DP[I+3]+DP[I+4]+DP[I+5]+E[DP+6])/6+V[I]

代码:

 

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 110 int a[N];
int n,t;
double dp[N]; int main()
{
int ttt=;
scanf("%d",&t);
while(t--)
{
ttt++;
scanf("%d",&n);
memset(dp,,sizeof(dp));
for(int i=;i<=n;i++)
{
scanf("%d",&a[i]);
}
dp[n]=a[n];
for(int i=n-;i>=;i--)
{
int up;
dp[i]=a[i];
if(n-i<)
{
up=n-i;
}
else
{
up=;
}
for(int j=;j<=up;j++)
{
dp[i]+=(1.0/up)*dp[i+j];
}
}
printf("Case %d: %.6lf\n",ttt,dp[]);
} }

 

 

T11 Easy Game

有一个子序列,有两个人轮流取,每次可以从左到右或从右到左取任意长度的数,使先取的与后取的差最大,并输出这个差。

一开始感觉是道博弈题啊。。。然后立马想到好像传说有博弈dp这种操作,但我太菜了不会啊!

不管怎么样,我们要对出题人有足够的信仰。

先想想策略吧,当前情况下无非是从左去或从右取两种操作,但如果只是取能取的最大值必然有后效性,诸如样例1

假设现在只有一个点,那么因为先手所以必须要拿,

如果有两个呢?考虑要么两个都拿,要么拿左边一个,要么拿右边一个。

如果有三个呢?要么全拿,要么拿左边两个,要么拿一个,而如果拿了一个,后者能取得的最优解就是去掉这一个以后拿两个的方案。显然右边也是如法炮制。

如果四个呢?对于先拿一个、两个的情况,后手的最优策略同样如上。

然后瞬间明了,这原来是道区间dp。。。

用dp[i][j]来表示i-j的区间中的最大差。

显然dp[i][i]就是a[i],然后假设sum[i][j]存的是i-j的前缀和(这只是为了方便理解,事实一维即可)

结合上面的推导

dp[i][j]=max{sum[i][j],sum[i][k]-dp[k+1][j],sum[k+1][j]-dp[i][k]}(i<=k<j)

 

代码:

 

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 110
#define INF 0x3f3f3f3f int dp[N][N],a[N],sum[N];
int n,t; int main()
{
int ttt=;
scanf("%d",&t);
while(t--)
{
ttt++;
memset(dp,,sizeof(dp));
memset(a,,sizeof(a));
scanf("%d",&n);
for(int i=;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-]+a[i];
}
for(int i=;i<=n;i++)
{
for(int j=;j<=n;j++)
{
dp[i][j]=-INF;
}
dp[i][i]=a[i];
}
for(int k=;k<=n;k++)
{
for(int i=;i+k-<=n;i++)
{
int j=i+k-;
dp[i][j]=sum[j]-sum[i-];
for(int mid=i;mid<j;mid++)
{
dp[i][j]=max(dp[i][j],sum[mid]-sum[i-]-dp[mid+][j]);
}
for(int mid=j;mid>i;mid--)
{
dp[i][j]=max(dp[i][j],sum[j]-sum[mid-]-dp[i][mid-]);
}
}
}
printf("Case %d: %d\n",ttt,dp[][n]);
}
return ;
}

 

 

T12 Fast Bit Calculation

一个数字在二进制下,如果一个1的右边还是1,则权值+1

诸如 11011 权值为2,11111权值为4

然后给你一个数x,求1-x之间所有数的权值之和

 

显然是数位dp,然而yyk大佬告诉我,我实在是太naive了,这道题可以用递推做!

想来也是,其实数位dp究其根源也只是一种暴力。

 

我们假设sum[i]表示1-i的权值和

那么

Sum[0]=0

Sum[1]=0

Sum[3]=1

Sum[7]=4

这几个数据还是可以手模出来的

然后对于下面该怎么办呢?

比如要算sum[15]

那么15比7要多了一个二进制位,所以说对于该位为0或1都存在sum[7]

所以sum[15]中首先包含两个sum[7]

而如果第四位和第三位都为1则会再多做一个贡献。而这些个数总共有2^(i-2)个,也就是前两位都确定了,后面几位的所有排列数

所以

Sum[15]=2*sum[7]+2^(i-2)

当然这个公式是针对每个2^i-1才有用的。

那普通的该怎么算呢?

显然如果对于一个二进制形式的数首位为1说明此位为0的情况是全被包含的

ans+=sum[i<<len]

然后删去这一位继续处理剩下的位数,就ok了!

 

代码:

 

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std; long long a[];
int n,t; void db()
{
a[]=;
a[]=;
a[]=;
a[]=;
for(int i=; i<=; i++)
{
a[i]=(<<(i-))+(a[i-]<<);
}
} long long get()
{
int temp=n;
long long ans=,x=;
for(int i=; i>=; i--)
{
if(temp&(x<<i))
{
n^=(x<<i);
ans+=a[i]+max((long long),n-(x<<(i-))+);
}
}
return ans;
} int main()
{
int ttt=;
db();
scanf("%d",&t);
while(t--)
{
ttt++;
scanf("%d",&n);
printf("Case %d: %lld\n",ttt,get());
}
return ;
}

 

 

 T13 Generating Palindromes

 

给你一个字符串,可以在任意位置插任意个字母,求至少插几个字母后会产生回文串?
这道题又是套路。。。
不过由于T8比较敷衍,还是好好讲讲吧。。。(然后讲错了)
对于每1个字母,都是回文串,并不需要任何操作。
2个字母,如果相等自然是0,不等是1
3个字母,如果左右相等,就是1个字母的方案。如果不相等,就是左边取2个的方案加补第三个或者右边取两个加补第一个。
4个字母,如果左右相等,就是第2个字母的方案。如果不相等,就是取左边3个加补第4个或、取右边3个加补第1个
综上所述
状态转移方程就可以推出来了
设dp[i][j]表示i-j之间全部处理成回文串的花费。

(s[i]==s[j])
Dp[i][j]=dp[i+1][j-1]
(s[i]!=s[j])
Dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1

 代码:

 

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 110 int dp[N][N],t,ttt; int main()
{
scanf("%d",&t);
while(t--)
{
char s[N];
ttt++;
memset(dp,,sizeof(dp));
cin>>s+;
int len=strlen(s+);
for(int i=len;i>=;i--)
{
for(int j=i+;j<=len;j++)
{
dp[i][j]=min(dp[i+][j],dp[i][j-])+;
if(s[i]==s[j])
{
dp[i][j]=min(dp[i][j],dp[i+][j-]);
} }
}
printf("Case %d: %d\n",ttt,dp[][len]);
} }

 

 

T14 A Refining Company

 

在新日暮里中生活着一群快乐的比利和VAN様,但由于他们天天摔跤,产生的DARK值已经快把新日暮里毁掉了!作为新日暮里的管理者,你需要把比利送向西边,VAN様送向北边,
已知新日暮里是一个N*M的矩形,每个1*1的格子里都有一定数量的比利和VAN様,现让你安排一些大巴车,这些大巴车的起点可以在任意位置,但方向只能向西或向北,不能拐弯。
如果向北,沿途的所有比利可以上车。如果向西,沿途的所有比利可以上车。为了防止他们越车摔跤,任意两个公交车的路线不能相交,求能安置好的比利和VAN様的最大数量

这是一道水题( ・´ω`・ ),思路非常简单。

几乎是一眼出方程系列:
Dp[i][j]=max(dp[i-1][j]+sum_line[i][j],dp[i][j-1]+sum_row[i][j])

 

代码:

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std; int n,m,t;
int a,b,suma[][],sumb[][];
long long dp[][]; int main()
{
int ttt=;
scanf("%d",&t);
while(t--)
{
ttt++;
memset(suma,,sizeof(suma));
memset(sumb,,sizeof(sumb));
memset(dp,,sizeof(dp));
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
scanf("%d",&a);
suma[i][j]=suma[i][j-]+a;
}
}
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
scanf("%d",&b);
sumb[i][j]=sumb[i-][j]+b;
}
}
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
dp[i][j]=max(dp[i][j-]+sumb[i][j],dp[i-][j]+suma[i][j]);
}
}
printf("Case %d: %d\n",ttt,dp[n][m]);
}
}

 

 

T15 Agent 47

开局只有一个人一把枪,装备全靠打。
告诉你n个小怪的血量以及他们的装备能对其他小怪的伤害,你一开始的武器只能对任何一个小怪造成1点伤害,打死一个小怪后能获得他的装备,并用来打其他的小怪。求消灭所有小怪的最少枪数。

因为n很小,只有15,所以理所应当地想到了状态压缩动态规划

假设一个状态sta中1表示已经被打死的小怪,0表示还没有打死

每次转移显然是从当前状态中挑出一个打死,花费是小怪血量除以当前所有武器中的最大值
然后更新加上这个被打死的小怪的状态

转移方程:设t为打死小i的最小次数
Dp[sta|(1<<i)]=min(dp[sta|(1<<i)],dp[sta]+t)

 

代码:

 

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N (1<<15)+10
#define INF 0x3f3f3f3f
using namespace std; int dp[N],hp[],map[][],n;
char line[]; int main()
{
int t,k=; scanf("%d",&t); while(t--)
{
k++; memset(dp,INF,sizeof(dp)); scanf("%d",&n); for(int i=;i<n;i++)
{
scanf("%d",&hp[i]);
dp[<<i]=hp[i];
} for(int i=;i<n;i++)
{
scanf("%s",line);
for(int j=;j<n;j++)
{
map[i][j]=line[j]-'';
}
} int maxsta=(<<n)-; for(int sta=;sta<=maxsta;sta++)
{
for(int j=;j<n;j++)
{
if(sta&(<<j))
{
continue;
} dp[sta|(<<j)]=min(dp[sta]+hp[j],dp[sta|(<<j)]); for(int x=;x<n;x++)
{
if((sta&(<<x))&&map[x][j])
{
int tot=hp[j]/map[x][j]; if(hp[j]%map[x][j])
{
++tot;
} dp[sta|(<<j)]=min(dp[sta]+tot,dp[sta|(<<j)]); }
}
}
} printf("Case %d: %d\n",k,dp[maxsta]); } }

 

 

 

 

T16 Palindrome Partitioning

给你一个字符串,要求你划分使所有的子串都是回文串,求最小划分数
好吧并不想多解释了,直接上代码:

 

#include<cstdio>
#include<algorithm>
#include<cstring>
#define INF 0x3f3f3f3f
#define N 1010
using namespace std; char s[N];
int dp[N]; bool judge(int i,int j)
{ while(i<=j)
{
if(s[i]!=s[j])
{
return ;
}
i++;
j--;
} return ;
} int main()
{
int t,ttt=;
scanf("%d",&t);
while(t--)
{
ttt++;
scanf("%s", s);
int len=strlen(s);
for(int i=;i<len;i++)
{ dp[i]=INF; for (int j=;j<=i;j++)
{ if(judge(j,i))
{ if(j == )
{
dp[i]=;
}
else
{
dp[i]=min(dp[i],dp[j-]+);
} }
}
}
printf("Case %d: %d\n",ttt,dp[len-]);
} }

 

 

T17Neighbor House

这道题比T1还水。。。不说了,

上代码:

 

#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std; int dp[][],a[][],n,t,ans; int main()
{
int ttt=;
scanf("%d",&t);
while(t--)
{
ttt++;
ans=;
scanf("%d",&n);
for(int i=;i<=n;i++)
{
dp[i][]=dp[i][]=dp[i][]=0x3f3f3f3f;
}
for(int i=;i<=n;i++)
{
for(int j=;j<;j++)
{
scanf("%d",&a[i][j]);
}
}
for(int i=;i<=n;i++)
{
dp[i][]=min(dp[i-][]+a[i][],dp[i-][]+a[i][]);
dp[i][]=min(dp[i-][]+a[i][],dp[i-][]+a[i][]);
dp[i][]=min(dp[i-][]+a[i][],dp[i-][]+a[i][]);
}
ans=min(dp[n][],min(dp[n][],dp[n][]));
printf("Case %d: %d\n",ttt,ans);
}
}

 

 

 

 

 

终于打完了。。。orz

dp百题大过关(第一场)的更多相关文章

  1. DP の 百题大过关(5/100)

      动态规划自古以来是DALAO凌虐萌新的分水岭,但有些OIer认为并没有这么重要--会打暴力,大不了记忆化.但是其实,动态规划学得好不好,可以彰显出一个OIerOIer的基本素养--能否富有逻辑地思 ...

  2. DP百题练(一)

    目录 DP百题练(一) 线性 DP 简述 Arithmetic Progressions [ZJOI2006]物流运输 LG1095 守望者的逃离 LG1103 书本整理 CH5102 移动服务 LG ...

  3. DP百题练(二)

    目录 DP百题练(二) 区间 DP NOI1995 石子合并 IOI1998 Polygon CH5302 金字塔 USACO06FEB Treats for the Cows G/S LG1043 ...

  4. DP百题练(三)

    目录 DP百题练(三) DP百题练(三) 不知不觉也刷了 50 道 DP 题了,感觉确实有较大的进步.(2020.3.20) 这里的 (三) 主要用来记录 DP 的各种优化(倍增.数据结构.斜率.四边 ...

  5. DP百题练索引

    就是一篇还在咕的文章 DP百题练(一) DP百题练(二) DP百题练(三)

  6. 线段树优化dp——牛客多校第一场I(好题)

    和两天做了两道数据结构优化dp的题,套路还是差不多的 题解链接! https://www.cnblogs.com/kls123/p/11221471.html 一些补充 其实这道题的dp[i]维护的不 ...

  7. HDU-6035 Colorful Tree(树形DP) 2017多校第一场

    题意:给出一棵树,树上的每个节点都有一个颜色,定义一种值为两点之间路径中不同颜色的个数,然后一棵树有n*(n-1)/2条 路径,求所有的路径的值加起来是多少. 思路:比赛的时候感觉是树形DP,但是脑袋 ...

  8. 2019HDU多校第一场1001 BLANK (DP)(HDU6578)

    2019HDU多校第一场1001 BLANK (DP) 题意:构造一个长度为n(n<=10)的序列,其中的值域为{0,1,2,3}存在m个限制条件,表示为 l r x意义为[L,R]区间里最多能 ...

  9. 校省选赛第一场A题Cinema题解

    今天是学校省选的第一场比赛,0战绩收工,死死啃着A题来做,偏偏一直WA在TES1. 赛后,才发现,原来要freopen("input.txt","r",stdi ...

随机推荐

  1. Angular2 Service实践

    引言: 如果说组件系统(Component)是ng2应用的躯体,那把服务(Service)认为是流通于组件之间并为其带来生机的血液再合适不过了.组件间通信的其中一种优等选择就是使用服务,在ng1里就有 ...

  2. 安装Vue2的devtools发生错误npm ERR! code EINTEGRITY npm ERR! sha1-HTFDXrRzR2Ew8Bv9zlMSCVgPsS0= integrity checksum failed when using sha1: wanted sha1-HTFDXrRzR2Ew8Bv9zlMSCVgPsS0= but got sha1-Z6BeTMF4nhAO6h5A

    1.github下载地址:https://github.com/vuejs/vue-devtools 2.下载好后进入vue-devtools-master工程  执行npm install ---- ...

  3. Java 基础语法

    一.关键字 定义:被 Java 赋予特殊含义的单词. 特点:基本上都是英文小写. 用于定义数据类型的关键字 基本数据类型 整数型(默认为 int) byte(8 位,1 字节,默认值是 0,最大存储数 ...

  4. Linux入门之常用命令(7)压缩

    compress filename 压缩   -d解压缩  *.Z bzip -d解压缩 -z压缩 *.bz2 bzcat filename .bz2 读取压缩文件内容 gzip -d解压缩  -#压 ...

  5. Treblecross 博弈SG值

    Treblecross is a two player game where the goal is to get three X in a row on a one-dimensional boar ...

  6. Cow Uncle 学习了叉积的一点运用,叉积真的不错

    Cow Uncle Time Limit: 4000/2000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others) SubmitSta ...

  7. 【技巧】datagrid锁定列后重新加载时出现错位问题的解决

    [问题描述]:有时候datagrid设置了锁定列后,在重新加载datagrid数据时,出现锁定列与非锁定列数据错位的问题,如图: [问题分析]:查看css样式我们发现,锁定的列和非锁定的列属于两个不同 ...

  8. Jquery插件之ajaxForm ajaxSubmit的理解用法(转)

    我们在平常使用Jquery异步提交表单,一般是在submit()中,使用$.ajax进行.比如: $(function(){ $('#myForm').submit(function(){ $.aja ...

  9. 错误:Cannot set property 'innerHTML' of null

    360浏览器代码编辑器里提示错误:Cannot set property 'innerHTML' of null 原因是代码执行时要调用的内容不存在

  10. springboot kafka集成(实现producer和consumer)

    本文介绍如何在springboot项目中集成kafka收发message. 1.先解决依赖 springboot相关的依赖我们就不提了,和kafka相关的只依赖一个spring-kafka集成包 &l ...