06-02考场随笔

考场随笔


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;
}
posted @ 2021-06-02 22:20  actypedef  阅读(70)  评论(0编辑  收藏  举报