sverer
作业:
A:
这道题很简单,不在赘述
代码:
#include <iostream>
using namespace std;
int main()
{
int n,i,m,t,ans;
while(cin>>n)
{
int a[32768]= {0};
t=(n+1)/2;
for(i=0; i<n; i++)
{
cin>>m;
a[m]++;
if(a[m]>=t)
ans=m;
}
cout<<ans<<endl;;
}
return 0;
}
B:三维迷宫
这道题和二维迷宫类似,不过换成了三维,都是使用BFS搜索。做过二维迷宫应该比较容易入手。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <queue>
using namespace std;
#define Max 50
bool maze[Max][Max][Max];
bool flag[Max][Max][Max];
int dir,h,w;
typedef struct Node{
int x;
int y;
int dir;
int deep;
}node;
int sx,sy,sdir,ex,ey,edir;
int Count;
bool bfs(){
queue<node> Que;
node point;
point.x=sx;
point.y=sy;
point.dir=sdir;
point.deep=0;
Que.push(point);
flag[sdir][sx][sy]=1;
int x,y,bdir,bdeep;
while(!Que.empty()){
point=Que.front();
Que.pop();
x=point.x,y=point.y;
bdir=point.dir,bdeep=point.deep;
if(x==ex && y==ey && bdir==edir){
Count=bdeep;
return true;
}
if(y-1>=1 && maze[bdir][x][y-1] && !flag[bdir][x][y-1]){
point.x=x;
point.y=y-1;
point.dir=bdir;
point.deep=bdeep+1;
flag[bdir][x][y-1]=1;
Que.push(point);
}
if(y+1<=w && maze[bdir][x][y+1] && !flag[bdir][x][y+1]){
point.x=x;
point.y=y+1;
point.dir=bdir;
point.deep=bdeep+1;
flag[bdir][x][y+1]=1;
Que.push(point);
}
if(x-1>=1 && maze[bdir][x-1][y] && !flag[bdir][x-1][y]){
point.x=x-1;
point.y=y;
point.dir=bdir;
point.deep=bdeep+1;
flag[bdir][x-1][y]=1;
Que.push(point);
}
if(x+1<=h && maze[bdir][x+1][y] && !flag[bdir][x+1][y]){
point.x=x+1;
point.y=y;
point.dir=bdir;
point.deep=bdeep+1;
flag[bdir][x+1][y]=1;
Que.push(point);
}
if(bdir-1>=1 && maze[bdir-1][x][y] && !flag[bdir-1][x][y]){
point.x=x;
point.y=y;
point.dir=bdir-1;
point.deep=bdeep+1;
flag[bdir-1][x][y]=1;
Que.push(point);
}
if(bdir+1<=dir && maze[bdir+1][x][y] && !flag[bdir+1][x][y]){
point.x=x;
point.y=y;
point.dir=bdir+1;
point.deep=bdeep+1;
flag[bdir+1][x][y]=1;
Que.push(point);
}
}
return false;
}
int main(){
while(scanf("%d%d%d",&dir,&h,&w),dir){
memset(maze,0,sizeof(maze));
memset(flag,0,sizeof(flag));
char str;
for(int i=1;i<=dir;i++){
getchar();
for(int j=1;j<=h;j++){
for(int k=1;k<=w;k++){
str=getchar();
if(str=='.')
maze[i][j][k]=1;
else if(str=='S'){
sdir=i;
sx=j;
sy=k;
maze[i][j][k]=1;
}
else if(str=='E'){
edir=i;
ex=j;
ey=k;
maze[i][j][k]=1;
}
}
getchar();
}
}
if(bfs())
printf("Escaped in %d minute(s).\n",Count);
else
printf("Trapped!\n");
}
return 0;
}
C:最大和连续子序列增强版
这是个最大和连续子序列问题的增强版,区别在于这道题有多个区间,一是我们要增加一维表示区间数目。用DP[i][j]表示前j个数分为i段能获得的最大和。状态转移方程为
dp[ i ][ j ]=max( dp[ i ][ j-1 ]+a[ j ] , max( dp[ i-1 ][ k ] )+a[ j ] ) i-1<=k<=j-1
这个方程的含义是:将第 aj 个数算在第 i 段的最后还是 第i段的开始。也就是aj和前面的一起在第 i 段还是 前面最多在第i-1段 aj在第i段。
如果是前者:dp[ i ][ j ] = dp[ i ][ j-1 ]+a[ j ]
如果是后者:dp[ i ][ j ] = max( dp[ i-1 ][ k ] )+a[ j ] 1<=k<=j-1
后者中 dp[ i-1 ][ k ]指的是,由于其区间不连续,则i-1段最后一个数可以是 1<=k<=j-1种的任何一个。取最大值即可。
然后这道题还要进行空间和时间上的优化,这里是用了滚动数组和记忆化。
用dp[j]表示表示到第j个的时候的最大和,解决空间的问题。用pre[ j-1 ]表示max( dp[ i-1 ][ k ] )以免再次遍历K ,解决了时间复杂度的问题。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>//HDU 1024
using namespace std;
const int maxn = 1e6;
const int inf = 0x3f3f3f3f;
long long dp[maxn + 5];
long long a[maxn +5];
long long pre[maxn + 5];
int n, m;
int main(){
while(~scanf("%d %d", &m, &n)){
memset(dp, 0, sizeof(dp));
memset(pre, 0, sizeof(pre));
for(int i = 1; i <= n; i++){
scanf(" %lld", &a[i]);
}
long long tmp;
for(int i = 1; i <= m; i++){
tmp = -inf;
for(int j = i; j <= n; j++){
dp[j] = max(dp[j - 1], pre[j - 1]) + a[j];
pre[j - 1] = tmp;
tmp = max(tmp, dp[j]);
}
}
printf("%lld\n", tmp);
}
return 0;
}
D:区间DP
这道题是括号匹配,套用区间DP的板子 ,本质是依次递推 ,最外层枚举 区间长度 ,内一层枚举 起点位置 ,最内层枚举 分割点位置。
状态方程:如果匹配 dp[i][j]=dp[i+1][j-1]+2;
然后枚举dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);。
#include<iostream>
#include<cstring>
#include<algorithm>//POJ 2955
using namespace std;
int dp[105][105];
string s;
bool match(char c1,char c2)
{
if(c1=='('&&c2==')') return true;
if(c1=='['&&c2==']') return true;
else return false;
}
void solve()
{
memset(dp,0,sizeof(dp));
int l=s.length();
for(int d=1;d<l;d++)
{
for(int i=0;i+d<l;i++)
{
int j=i+d;
if(match(s[i],s[j]))
{
dp[i][j]=dp[i+1][j-1]+2;
}
for(int k=i;k<=j-1;k++)
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);
}
}
}
int main()
{
while(1)
{
cin>>s;
if(s[0]=='e') break;
solve();
cout<<dp[0][s.length()-1]<<endl;;
}
return 0;
}
E:状压DP
将15个作业压缩成一个数,每一位二进制代表一门课是否完成。然后开始DP。
枚举从1到bit(1<<n)的每一个状态i;
然后枚举每一门课j(倒叙枚举,因为输入是按照字典序递增输入的),检查状态i是否完成了第j门课通过与运算,如果完成了第j门课,计算不完成第j门课时所扣的分加上完成第j门课所扣的分是否比当前状态i所扣的分少,如果少于则进行更新分数和时间,并记下j作为路径输出。
#include <iostream>
#include <cstdio>
const int inf=0x3f3f3f3f;
const int MAX=(1<<15)+10;
using namespace std;
int n,dp[MAX],t[MAX],pre[MAX],ddl[20],len[20];
char s[20][110];
void output(int x)
{
if(!x)return;
output(x-(1<<pre[x]));
printf("%s\n",s[pre[x]]);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%s%d%d",s[i],&ddl[i],&len[i]);
int bit=1<<n;
for (int i=1;i<bit;i++)
{
dp[i]=inf;
for (int j=n-1;j>=0;j--)
{
int temp=1<<j;
if (!(i&temp))
continue;
//扣分: 上一个状态的时间+长度-截止时间
int score=t[i-temp]+len[j]-ddl[j];
if (score<0)
score=0;
if (dp[i]>dp[i-temp]+score)
{
dp[i]=dp[i-temp]+score;
t[i]=t[i-temp]+len[j];
pre[i]=j;
}
}
}
printf("%d\n",dp[bit-1]);
output(bit-1);
}
return 0;
}
实验题:
实验题第一道很简单就不说了,第二题是矩阵旋转,还是方阵,跟上上周的魔方一样,可以用多次旋转90度取代旋转180度和270度即可。
第三题是一道字符串的题,但是直接去做很难。它看起来是回文字符串,但是基本跟回文字符串不搭边。
我们从反方向入手,把总的方案数求出来,可由排列组合直到总方案数是n*(n-1)/2
,然后求除不符合规则的字符串数目。
由题干可知,不符合规则的字符串只有两种类型,xxxy 和 xyyy。其他的均符合要求。所以我们只需要求除xxxy和xyyyy这两种数目即可,我们可以正序遍历一边然后倒叙遍历一遍。还有要注意要把xy去重,因为正着算了一遍,倒叙的时候又算了一遍。
#include<iostream>
using namespace std;
long long int n;
long long int ans;
long long int lenth=1;
string s;
int main()
{
cin>>n; cin>>s;
ans=n*(n-1)/2;
for(int i=1;i<n;i++)//AAAAB
{
if(s[i]==s[i-1])
lenth++;
else
{
ans=ans-lenth;
lenth=1;
}
}
lenth=1;
for(int i=n-2;i>=0;i--)
{
if(s[i]==s[i+1])
lenth++;
else
{
ans=ans-lenth;
lenth=1;
}
}
for(int i=1;i<n;i++)
if(s[i]!=s[i-1])
ans++;
cout<<ans<<endl;
return 0;
}
另外附上AB题的代码:
A:
#include<iostream>
using namespace std;
int last=0x3f3f3f3f;
int main (){
int n;cin>>n;
int p=0;int ans=0;
for(int i=0;i<n;i++)
{
cin>>p;
if(p!=last)
{
ans++;last=p;
}
else continue;
}
cout<<ans<<endl;
return 0;
}
B:
#include<iostream>
using namespace std;
#define maxn 32
#define maxm 32
int n,m;
int G[maxn][maxm];
int mark[maxn][maxm];
int main (){
cin>>n>>m;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cin>>G[i][j];
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(j-1>=0&&j+1<=m-1)
{
if(G[i][j-1]==G[i][j]&&G[i][j]==G[i][j+1])
{
mark[i][j-1]=-1;mark[i][j]=-1;mark[i][j+1]=-1;
}
}
}
}
for(int j=0;j<m;j++)
{
for(int i=0;i<n;i++)
{
if(i>=1&&i<=n-2)
{
if(G[i-1][j]==G[i][j]&&G[i][j]==G[i+1][j])
{
mark[i-1][j]=-1;mark[i][j]=-1;mark[i+1][j]=-1;
}
}
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(mark[i][j]!=-1)
cout<<G[i][j];
else
cout<<0;
if(j<m-1)cout<<" ";
}
if(i<n-1) cout<<endl;
}
return 0;
}