考场随笔
A.多边形
描述
多边形是一个人玩的游戏。开始时在一含有 NN 个节点的多边形上玩,如图一所示,在此图中,N=4。每一个节点以一个整数标示;每一条边则以符号 +(加)或符号 *(乘)标示。这些边的编号从 1 到 N。
下第一步时,先移除任何一边,之后的下法如下:
‧任取一边,称之为 E,并令其两端的节点为 V1 及 V2
‧先执行 E 与 V1,V2 之运算,并将结果标示于一个新节点,用来取代旧节点及边。
当所有的边都被移除时,游戏即结束,而得分 (score) 则为最后留下之节点标示之值。
输入格式
描述一个含有 N 个节点的多边形。此文件含有二行。第 1 行是数字 N。第 2 行含有编号 1 到 N 边的标示。中间交错着节点的标示(首先是编号为 1 和 2 的两边之间的节点,接着是编号为 2 和 3 的两边之间的节点,以此类推,最后是编号为 N 和 1 的兩边之间的节点),均以一个空格隔开。一个边的标示即是字母 t(表示 +)或是字母 x(表示 *)。
输出格式
针对输入的多边形,你的程序必须输出最高的可能得分。
思路
图一共有$ N-1 $种情况
有点像个区间DP?
f[i][j]
表示某种形态的图中,合并完第
i
i
i到第
j
j
j个点的最高得分
f[l][r]=max{calc(f[l][k],f[k+1][r])}
复杂度: 图 的 种 类 ∗ 枚 举 区 间 长 度 ∗ 枚 举 左 端 点 ∗ 枚 举 断 点 图的种类*枚举区间长度*枚举左端点*枚举断点 图的种类∗枚举区间长度∗枚举左端点∗枚举断点
O ( N 4 ) O(N^4) O(N4)
应该没什么问题吧
代码
/*************************************************************************
> File Name: 多边形.cpp
> Author: Typedef
> Mail: 1815979752@qq.com
> Created Time: 2021年06月02日 星期三 19时04分31秒
> Tags:
************************************************************************/
#include<bits/stdc++.h>
#define inf 2147483647
using namespace std;
typedef long long ll;
const int N=55;
char c[N],cal[N];
ll v[N],val[N],f[N][N],ans=-inf;
int n;
/*
void print(){
for(int i=1;i<n;i++){
cout<<val[i]<<cal[i];
}
cout<<val[n]<<endl;
return;
}
*/
void init(int t){
memset(val,0,sizeof(val));
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j]=-inf;
for(int i=1;i<=n;i++) val[i]=' ';
for(int i=t+1;i<=n+t;i++)
cal[i-t]=c[i];
for(int i=1;i<=n;i++)
val[i]=v[t+i-1];
// print();
return;
}
ll calc(ll a,ll b,ll p){
if(cal[p]=='t') return a+b;
else return a*b;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
char op;
ll num;
cin>>op>>num;
c[i]=op;
v[i]=num;
}
for(int i=n+1;i<=2*n;i++) c[i]=c[i-n],v[i]=v[i-n];
for(int t=1;t<=n;t++){
init(t);
for(int len=1;len<=n;len++)
for(int l=1;l<=n;l++){
int r=l+len-1;
if(len==1){
f[l][r]=val[l];
continue;
}
for(int k=l;k<r;k++)
f[l][r]=max(f[l][r],calc(f[l][k],f[k+1][r],k));
}
ans=max(ans,f[1][n]);
}
printf("%lld\n",ans);
return 0;
}
B. 龙妹妹买牛奶
描述
龙妹妹一次喝光了所有的牛奶,一觉醒来后他决定去买牛奶。他居住的城市的具有某些奇妙的性质:
1、城市可以看作由 N−1 条路连接着的 N 家牛奶店(对于牛奶龙来说,其他设施真是毫无意义)。
2、每条路长度均为 1。
3、龙妹妹每次会选择一家店,买光里面所有牛奶。
4、龙妹妹一旦买了某家店的牛奶,和这家店距离在 K 以内的店均会立即改行卖核桃仁。233…
龙妹妹想知道,通过合理的购买策略,最多可以买到多少家店的牛奶。
输入格式
第一行两个正整数 N,K。
之后 N−1 行,每行两个整数,表示一条路连接的两家牛奶店的编号。所有点的标号在 [1,n] 内。
输出格式
一个整数,龙妹妹最多买到多少家店的牛奶。
思路
本题中 K K K的值为 1 1 1或 2 2 2
树形DP吧(大概
每个点都要向下看深度为 K K K内是否有牛奶店没买了
如果没有,那么可以传递到买了这一状态如果买了,那么可以传递到没买这一状态
K = 1 K=1 K=1的话好说
f[i][0/1]
表示
i
i
i这家店不买/买的最大收益
K = 2 K=2 K=2的话
那就对于根u,枚举它的儿子,从中找出最大的,买的儿子加上其他儿子不选的这种情况,与所有儿子都不选的情况比较
代码
/*************************************************************************
> File Name: 龙妹妹买牛奶.cpp
> Author: Typedef
> Mail: 1815979752@qq.com
> Created Time: 2021年06月02日 星期三 19时04分51秒
> Tags:
************************************************************************/
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7;
int n,k;
int f[N][2];
bool vis[N];
vector<int> son[N];
void dfs1(int u){
vis[u]=1;
f[u][1]=1;
for(int i=0;i<son[u].size();i++){
int v=son[u][i];
if(vis[v]) continue;
dfs1(v);
f[u][1]+=f[v][0];
f[u][0]+=max(f[v][1],f[v][0]);
}
}
void dfs2(int u){
vis[u]=1;
f[u][1]=1;
int tmp1=0,tmp2=0;
for(int i=0;i<son[u].size();i++){
int v=son[u][i];
if(vis[v]) continue;
dfs2(v);
for(int j=0;j<son[v].size();j++){
int x=son[v][j];
if(vis[x]) continue;
f[u][1]+=f[x][0];
}
tmp1=max(f[v][1]-f[v][0],tmp1);
tmp2+=f[v][0];
}
f[u][0]=max(tmp1+tmp2,tmp2);
}
void solve1(){
for(int i=1;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
son[a].push_back(b);
son[b].push_back(a);
}
memset(f,0,sizeof(f));
dfs1(1);
printf("%d\n",max(f[1][1],f[1][0]));
return;
}
void solve2(){
for(int i=1;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
son[a].push_back(b);
son[b].push_back(a);
}
memset(f,0,sizeof(f));
dfs2(1);
printf("%d\n",max(f[1][1],f[1][0]));
}
int main(){
scanf("%d%d",&n,&k);
if(k==1) solve1();
else solve2();
return 0;
}
C.不要62
描述
题目描述
杭州人称那些傻乎乎粘嗒嗒的人为 62(音:laoer).
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
不吉利的数字为所有含有 4 或 62 的号码。例如:62315, 73418, 88914 都属于不吉利号码。但是,61152 虽然含有 6 和 2, 但不是 62 连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照区间号 [n,m] , 推断出交管局又能给多少辆新的士车上牌照了。
输入格式
输入的都是整数对 n,m, 如果遇到都是 0 的整数对,则输入结束。
输出格式
对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。
思路
f[i][j]
表示
i
i
i为数,开头数为
j
j
j的方案数
然后经典数位DP
代码
/*************************************************************************
> File Name: 不要62.cpp
> Author: Typedef
> Mail: 1815979752@qq.com
> Created Time: 2021年06月02日 星期三 19时05分05秒
> Tags:
************************************************************************/
#include<bits/stdc++.h>
using namespace std;
const int N=233,M=9177;
int f[N][N];
int p[N];
int n,m,res=0;
void init(){
memset(f,0,sizeof(f));
f[0][0]=1;
for(int i=1;i<=7;i++)
for(int j=0;j<=9;j++)
for(int k=0;k<=9;k++)
if(j!=4&&(j!=6||k!=2))
f[i][j]+=f[i-1][k];
return;
}
int dp(int n){
int idx=0;
memset(p,0,sizeof(p));
while(n){
p[++idx]=n%10;
n/=10;
}
p[idx+1]=0,res=0;
for(int i=idx;i>=1;i--){
for(int j=0;j<p[i];j++)
if(j!=4&&(p[i+1]!=6||j!=2))
res+=f[i][j];
if(p[i]==4||(p[i+1]==6&&p[i]==2)) break;
}
return res;
}
int main(){
init();
while(cin>>n>>m&&(n||m)) printf("%d\n",dp(m+1)-dp(n));
return 0;
}