- 题目:1001 数塔
- 思路:DP初步,由下向上得到每个结点的最大权值,时间复杂度是O(n^2)。
- 代码如下:
#include<bits/stdc++.h>
using namespace std;
int dp[110][110];
int main()
{
int n;
cin>>n;
while(n--)
{
int t;
cin>>t;
for(int i=1;i<=t;i++)
{
for(int j=1;j<=i;j++)
{
cin>>dp[i][j];
}
}
for(int i=t-1;i>=1;i--)
{
for(int j=0;j<=i;j++)
{
dp[i][j]=dp[i][j]+max(dp[i+1][j],dp[i+1][j+1]);
}
}
printf("%d\n",dp[1][1]);
}
return 0;
}
- 题目:1002 最少拦截系统
- 思路:DP初步,要求最少需要配置多少套拦截系统,按顺序依次贪心也可以做(虽然还是不能明确证明其正确性,证明:先对样例模拟一下,有感觉确实是贪心最优就是全局最优。反证法:如果并不采取每次都是最优的拦截,那么一定就会需要更多的拦截系统来拦截本应贪心的部分)。
- DP思路:若后面的数有大于前面的数的,那么后面的那个数肯定需要另一个拦截系统,这样的话,我们要求的就是最少的非上升子序列数,从而转化为求最长上升子序列的长度。怎么求呢???动态规划的思想,将每个数所在位置的其所在“上升子序列中的长度都设为DP[i]=1”,接下去在双重循环中更新第i个数(第i个数不影响前面的i-1个数的dp)。
- 代码如下:
#include<bits/stdc++.h>
using namespace std;
int num[100010];
int dp[10010];
int main()
{
int n;
while(cin>>n)
{
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++)
{
cin>>num[i];
dp[i]=1;
}
for(int i=0;i<n;i++)
{
for(int j=0;j<i;j++)
{
if(num[i]>num[j])
{
dp[i]=max(dp[i],dp[j]+1);
}
}
}
sort(dp,dp+n,greater<int>());
cout<<dp[0]<<endl;
}
}
- 题目:1003 搬寝室
- 题意:DP初步,给定包含n个数表示n件行李的重量,要搬k次行李,每次搬2件,并使得这两件行李的重量差的平方尽可能小,给出搬k次之后所消耗的最少体力。
- DP思路:DP初步,看来确实快忘记DP算法了,看了杭电的视频来回忆。是这样的:DP问题要把大问题分解为小问题,n个物品 搬k次---->n个物品搬1次---->2…3…4…5…6…个物品搬1次,由此过程,就可以模拟出DP公式,尽管一开始并不知道整个大问题怎么实现,但知道每个小问题怎么实现后,并且每个小问题都覆盖了所有情况并且无重复,这样整个大问题的求解思路也就清晰了。
- 思路详解:设数组DP[1005][2005],1005表示搬的趟数,2005表示搬的第几个物品。n个物品搬1次:dp[1][i]=min(dp[1][i-1],dp[0][i-2]+(w[i]-w[i-1])^2),n个物品搬2次: dp[2][i]=min(dp[2][i-1],dp[1][i-2]+(w[i]-w[i-1])^2)…搬k次: dp[1][i]=min(dp[k][i-1],dp[k-1][i-2]+(w[i]-w[i-1])^2)
同时,要注意初始化dp时,初始化为一个大数,因为我们要求的是所耗体力的最小值,同时还要注意初始化dp[0][0-n]整个数组为0,因为第0次搬运所耗体力为0,这样,在循环中用DP就AC了。 - 代码如下:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,k;
int dp[1005][2005];
int w[2010];
while(scanf("%d%d",&n,&k)!=EOF)
{
memset(dp,1000000,sizeof(dp));
for(int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
}
for(int j=0;j<=n;j++)
{
dp[0][j]=0;
}
sort(w+1,w+1+n);
for(int i=1;i<=k;i++)
{
for(int j=2*i;j<=n;j++)
{
dp[i][j]=min(dp[i][j-1],dp[i-1][j-2]+(w[j]-w[j-1])*(w[j]-w[j-1]));
}
}
printf("%d\n",dp[k][n]);
}
return 0;
}
- 题目:1005 丑数
- 题意:DP初步+打表(其实一开始比较难看出来是DP而且这种打表的方式也很奇怪,思维难度高,难想出来,但明白思路之后就轻松了)
- DP思路:并非以往的用一个式子就可以做出来,这里DP要用到多个变量控制。核心是:用DP思路打表时,第i个符合题意的数要利用前面已经算出的所有数(因为有且只有它们只包含2,3,5,7四个因子)去乘(2,3,5,7)得到最小的数作为第i个数。
- 思路详解:设p1,p2,p3,p4初值都为1,a[1]=1;接下去 a[++k]=min(min(2a[p1],3a[p2]),min(5a[p3],7a[p4]));同时用得到的a[k]这个数与之前的这四个数作比较,相等则更新对应的p,使其+1,对应到下一个最小的数。(这里再多解释一下(2a[p1],3a[p2],5a[p3],7a[p4])代表的意思,这些数就代表着可能的所有使第i个数最小的所有情况,做到了剪枝的操作。)
- 代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[6000];
int main()
{
a[1]=1;
ll k=1;
ll p1=1,p2=1,p3=1,p4=1;
while(k<6000)
{
a[++k]=min(min(2*a[p1],3*a[p2]),min(5*a[p3],7*a[p4]));
if(a[k]==2*a[p1])p1++;
if(a[k]==3*a[p2])p2++;
if(a[k]==5*a[p3])p3++;
if(a[k]==7*a[p4])p4++;
}
int n;
while(cin>>n)
{
if(n==0)break;
printf("%lld\n",a[n]);
}
return 0;
}
- 题目:1006 Bone Collector
- 思路:典型的背包问题,容量为V的背包要放入骨头,每块骨头都有权值,要求使得背包能够装的石头的总权值尽可能大。双重循环,第一遍遍历每一块石头(和顺序无关,不用排序),第二遍对整个体积由大到小进行遍历(为什么由大到小进行遍历呢??反过来想一下,如果是有小到大遍历,那么对于公式:
dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
随着j的不断增大,如果c[i]==5,j由0–>100,那么j = =10时,之前j= =5所进行的更改已经用过此物品了,所以接下去会重复利用此物品–》这就拓展到完全背包了)(反而从V---->c[i]遍历,就可以使得只利用一次该物品,这才是标准的0/1背包) - 代码如下:
#include<bits/stdc++.h>
using namespace std;
int dp[1001],w[1000],c[1000];
int main()
{
int num,n,V,i,j;
scanf("%d",&num);
while(num--)
{
scanf("%d%d",&n,&V);
for(i=1;i<=n;i++)scanf("%d",&w[i]);
for(i=1;i<=n;i++)scanf("%d",&c[i]);
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
for(j=V;j>=c[i];j--)
dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
printf("%d\n",dp[V]);
}
}
- 题目:1008 珍惜现在,感恩生活
- 思路:典型的背包问题,既不是0/1背包也不是完全背包,每个物品都赋予一定的数量,其可以转化为0/1背包,就是再用一个循环来处理数组num[]。
- 代码如下:
#include<bits/stdc++.h>
using namespace std;
int V,m;
int c[210],v[210],num[210];
int dp[210];
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>V>>m;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&c[i],&v[i],&num[i]);
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=m;i++)
{
for(int j=V;j>=0;j--)
{
for(int k=1;k<=num[i];k++)
{
if(k*c[i]<=j)
{
dp[j]=max(dp[j],dp[j-k*c[i]]+v[i]*k);
}
}
}
}
printf("%d\n",dp[V]);
}
return 0;
}
- 题目:1009 寒冰王座
- 思路:典型的背包问题,转化为完全背包,有两个物品,设其value与重量相等,这样的话最后装到体积为V的背包中的DP[V]与V只差就是答案。
- 代码如下:
#include<bits/stdc++.h>
using namespace std;
int dp[10010];
int main()
{
int t;
cin>>t;
int v[2]={150,200};
int c[2]={150,200};
while(t--)
{
int vv;
cin>>vv;
memset(dp,0,sizeof(dp));
for(int i=0;i<=1;i++)
{
for(int j=c[i];j<=vv;j++)
{
dp[j]=max((dp[j-c[i]]+v[i]),dp[j]);
}
}
printf("%d\n",vv-dp[vv]);
}
}
- 题目:1010 减肥记
- 思路:典型的背包问题,完全背包,在0/1背包问题的第二层循环中改一下j的顺序即可。
- 代码如下:
#include<bits/stdc++.h>
using namespace std;
int dp[100010];
int c[110],v[110];
int main()
{
int n,W;
while(cin>>n)
{
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
scanf("%d%d",&v[i],&c[i]);
}
scanf("%d",&W);
for(int i=1;i<=n;i++)
{
for(int j=c[i];j<=W;j++)
{
dp[j]=max(dp[j],dp[j-c[i]]+v[i]);
}
}
printf("%d\n",dp[W]);
}
return 0;
}
- 题目:1011 畅通工程续
- 思路:最短路径初步,基础的Dijkstra模板,练习PAT的时候写了好多好多基础的Dijkstra了。
- 代码如下:
#include<bits/stdc++.h>
using namespace std;
int G[210][210];
bool vis[210];
int dist[210];
const int inf=0x3fffffff;
int st,en;
int n,m;
void Dijkstra(int start)
{
fill(dist,dist+210,inf);
fill(vis,vis+210,false);
dist[start]=0;
for(int i=0;i<n;i++)
{
int u=-1,MIN=inf;
for(int j=0;j<n;j++)
{
if(vis[j]==false&&dist[j]<MIN)
{
u=j;
MIN=dist[j];
}
}
if(u==-1)return;
vis[u]=true;
for(int v=0;v<n;v++)
{
if(vis[v]==false)
{
if(dist[u]+G[u][v]<dist[v])
{
dist[v]=dist[u]+G[u][v];
}
}
}
}
}
int main()
{
int a,b,dis;
while(scanf("%d%d",&n,&m)!=EOF)
{
fill(G[0],G[0]+210*210,inf);
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&dis);
G[a][b]=G[b][a]=min(dis,G[a][b]);
}
scanf("%d%d",&st,&en);
Dijkstra(st);
if(dist[en]!=inf)
printf("%d\n",dist[en]);
else
printf("-1\n");
}
return 0;
}
- 题目:1014 A strange lift
- 思路:BFS初步。给定初始状态和状态转移条件,通过在BFS中队列的应用,用VIS数组进行剪枝,便可以遍历全部的情况,或到达终点,break输出,或队列为空依旧不能到达终点,则输出-1.
- 代码如下:
#include<bits/stdc++.h>
using namespace std;
bool vis[210];
struct node
{
int k;
int steps;
}Floor[410];
int n,st,en;
int BFS()
{
queue<int>qu;
int cur,next;
Floor[st].steps=1;
vis[st]=true;
qu.push(st);
int flag=1;
while(!qu.empty())
{
cur=qu.front();
qu.pop();
if(cur==en)
{
printf("%d\n",Floor[cur].steps-1);
flag=0;
break;
}
if(Floor[cur].k+cur<=n)
{
if(vis[Floor[cur].k+cur]==false)
{
Floor[Floor[cur].k+cur].steps=Floor[cur].steps+1;
vis[Floor[cur].k+cur]=true;
qu.push(Floor[cur].k+cur);
}
}
if(cur-Floor[cur].k>=1)
{
if(vis[cur-Floor[cur].k]==false)
{
Floor[cur-Floor[cur].k].steps=Floor[cur].steps+1;
vis[cur-Floor[cur].k]=true;
qu.push(cur-Floor[cur].k);
}
}
}
if(flag==1)
printf("-1\n");
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
if(n==0)break;
fill(vis,vis+210,false);
scanf("%d%d",&st,&en);
for(int i=1;i<=n;i++)
{
cin>>Floor[i].k;
}
BFS();
}
}
- 题目:1015 非常可乐
- 思路:BFS初步。三个杯子中可乐的体积是状态,通过不断地倒可乐进行状态转移,用的思维简单但代码量长的六种情况写的状态转移,第一次出现的状态标记为true,若到达两杯体积相同一杯为空则输出倒可乐的次数,若队列为空依旧没有到达则输出NO。
- 代码如下:(没有用函数,确实该练一下用函数要怎么写,省赛之后再来补用函数的代码)
#include<bits/stdc++.h>
using namespace std;
bool vis[110][110];
struct node
{
int a,b,c;
int steps;
}Floor;
int A,B,C;
void BFS()
{
queue<node>qu;
node cur,next;
cur.a=A;cur.b=0;cur.c=0;
cur.steps=0;
vis[cur.a][cur.b]=true;
qu.push(cur);
int flag=0;
while(!qu.empty())
{
cur=qu.front();
qu.pop();
if(((cur.a==cur.b)&&cur.c==0)||((cur.c==cur.b)&&cur.a==0)||((cur.a==cur.c)&&cur.b==0))
{
flag=1;
printf("%d\n",cur.steps);
break;
}
next=cur;
if(next.a>0&&B-next.b>0)
{
if(next.a>=(B-next.b))
{
next.a=next.a-(B-next.b);
next.b=B;
}
else if(next.a<(B-next.b))
{
next.b=next.b+next.a;
next.a=0;
}
vis[next.a][next.b]=true;
next.steps=cur.steps+1;
qu.push(next);
}
next=cur;
if(next.b>0&&C-next.c>0)
{
if(next.b>=(C-next.c))
{
next.b=next.b-(C-next.c);
next.c=C;
}
else if(next.b<(C-next.c))
{
next.c=next.c+next.b;next.b=0;
}
if(vis[next.a][next.b]==false)
{
next.steps=cur.steps+1;
vis[next.a][next.b]=true;
qu.push(next);
}
}
next=cur;
if(next.a>0&&C-next.c>0)
{
if(next.a>=(C-next.c))
{
next.a=next.a-(C-next.c);
next.c=C;
}
else if(next.a<(C-next.c))
{
next.c=next.c+next.a;next.a=0;
}
if(vis[next.a][next.b]==false)
{
next.steps=cur.steps+1;
vis[next.a][next.b]=true;
qu.push(next);
}
}
next=cur;
if(next.b>0&&A-next.a>0)
{
if(next.b>=(A-next.a))
{
next.b=next.b-(A-next.a);
next.a=A;
}
else if(next.b<(A-next.a))
{
next.a=next.a+next.b;next.b=0;
}
if(vis[next.a][next.b]==false)
{
next.steps=cur.steps+1;
vis[next.a][next.b]=true;
qu.push(next);
}
}
next=cur;
if(next.c>0&&A-next.a>0)
{
if(next.c>=(A-next.a))
{
next.c=next.c-(A-next.a);
next.a=A;
}
else if(next.c<(A-next.a))
{
next.a=next.a+next.c;next.c=0;
}
if(vis[next.a][next.b]==false)
{
next.steps=cur.steps+1;
vis[next.a][next.b]=true;
qu.push(next);
}
}
next=cur;
if(next.c>0&&B-next.b>0)
{
if(next.c>=(B-next.b))
{
next.c=next.c-(B-next.b);
next.b=B;
}
else if(next.c<(B-next.b))
{
next.b=next.b+next.c;next.c=0;
}
if(vis[next.a][next.b]==false)
{
next.steps=cur.steps+1;
vis[next.a][next.b]=true;
qu.push(next);
}
}
}
if(flag==0)
{
printf("NO\n");
}
}
int main()
{
while(scanf("%d%d%d",&A,&B,&C)!=EOF)
{
if(A==0&&B==0&&C==0)break;
fill(vis[0],vis[0]+110*110,false);
BFS();
}
}
- 题目:1016 Knight Moves
- 思路:BFS初步。很经典的其实在棋盘中走步问题,对所有的状态转移进行循环处理,用VIS数组剪枝,注意判断边界情况。
- 代码如下:(虽然很简单,但好久不写一次AC好快乐哈哈哈哈哈哈哈)
#include<bits/stdc++.h>
using namespace std;
int vis[10][10];
int st_x,st_y,en_x,en_y;
string st,en;
int zou[8][2]={{1,2},{2,1},{1,-2},{2,-1},{-1,-2},{-2,-1},{-1,2},{-2,1}};
struct node
{
int x,y;
int steps;
};
void BFS()
{
node cur,next;
fill(vis[0],vis[0]+10*10,false);
queue<node>qu;
cur.x=st_x;
cur.y=st_y;
cur.steps=0;
qu.push(cur);
while(!qu.empty())
{
cur=qu.front();
qu.pop();
if(cur.x==en_x&&cur.y==en_y)
{
printf("To get from ");
cout<<st;
printf(" to ");
cout<<en;
printf(" takes %d knight moves.\n",cur.steps);
break;
}
for(int i=0;i<8;i++)
{
next.x=cur.x+zou[i][0];
next.y=cur.y+zou[i][1];
if(next.x>=1&&next.x<=8&&next.y>=1&&next.y<=8&&vis[next.x][next.y]==false)
{
next.steps=cur.steps+1;
vis[next.x][next.y]=true;
qu.push(next);
}
}
}
}
int main()
{
while(cin>>st>>en)
{
st_x=st[0]-'a'+1;
st_y=st[1]-'0';
en_x=en[0]-'a'+1;
en_y=en[1]-'0';
BFS();
}
}
- 题目:1017 Rescue
- 思路:BFS初步。这里用到了优先队列,需要在结构体里面重载运算符<。也是很经典的带不同权值的BFS问题。
- 知识点:优先队列:priority_queue,因为优先队列在每次有元素入队是都会进行一次排序,默认将权值大的数放在前面,因此在结构体中重写运算符<时,写成如下所示。
friend bool operator < (node a,node b)
{
return a.steps>b.steps;
}
- 代码如下:(注意是多组输入,PAT一般不会这样,但省赛的时候也要注意一下)
#include<bits/stdc++.h>
using namespace std;
int zou[4][2]={{0,1},{0,-1},{-1,0},{1,0}};
int vis[210][210];
char m[210][210];
int N,M;
int st_x,st_y,en_x,en_y;
struct node
{
int x,y;
int steps;
friend bool operator < (node a,node b)
{
return a.steps>b.steps;
}
};
void BFS()
{
fill(vis[0],vis[0]+210*210,false);
node cur,next;
cur.x=st_x;
cur.y=st_y;
cur.steps=0;
vis[cur.x][cur.y]=true;
priority_queue<node>qu;
qu.push(cur);
while(!qu.empty())
{
cur=qu.top();
qu.pop();
if(m[cur.x][cur.y]=='r')
{
printf("%d\n",cur.steps);
return;
}
for(int i=0;i<4;i++)
{
next.x=cur.x+zou[i][0];
next.y=cur.y+zou[i][1];
if(next.x>=1&&next.x<=N&&next.y>=1&&next.y<=M&&vis[next.x][next.y]==false&&m[next.x][next.y]!='#')
{
if(m[next.x][next.y]=='.'||m[next.x][next.y]=='r')
next.steps=cur.steps+1;
if(m[next.x][next.y]=='x')
next.steps=cur.steps+2;
vis[next.x][next.y]=true;
qu.push(next);
}
}
}
printf("Poor ANGEL has to stay in the prison all his life.\n");
}
int main()
{
while(cin>>N>>M)
{
getchar();
for(int i=1;i<=N;i++)
{
for(int j=1;j<=M;j++)
{
scanf("%c",&m[i][j]);
if(m[i][j]=='a')
{
st_x=i;st_y=j;
}
}
getchar();
}
BFS();
}
}