2169: 跳房子(NOIP 2017 PJT4)
题目描述
跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。
跳房子的游戏规则如下:
在地面上确定一个起点,然后在起点右侧画 n� 个格子,这些格子都在同一条直线上。每个格子内有一个数字(整数),表示到达这个 格子能得到的分数。玩家第一次从起点开始向右跳,跳到起点右侧的一个格子内。第二次再从当前位置继续向右跳,依此类推。规则规定:
玩家每次都必须跳到当前位置右侧的一个格子内。玩家可以在任意时刻结束游戏,获得的分数为曾经到达过的格子中的数字之和。
现在小 R� 研发了一款弹跳机器人来参加这个游戏。但是这个机器人有一个非常严重的缺陷,它每次向右弹跳的距离只能为固定的 d� 。小 R� 希望改进他的机器人,如果他花 g� 个金币改进他的机器人,那么他的机器人灵活性就能增加 g� ,但是需要注意的是,每 次弹跳的距离至少为 11 。具体而言,当 g<d�<� 时,他的机器人每次可以选择向右弹跳的距离为 d−g,d−g+1,d−g+2�−�,�−�+1,�−�+2 ,…, d+g−2�+�−2 , d+g−1�+�−1 , d+g�+� ;否则(当 g≥d�≥� 时),他的机器人每次可以选择向右弹跳的距离为 11 , 22 , 33 ,…, d+g−2�+�−2 , d+g−1�+�−1 , d+g�+� 。
现在小 R� 希望获得至少 k� 分,请问他至少要花多少金币来改造他的机器人。
输入
第一行三个正整数 n� , d� , k� ,分别表示格子的数目,改进前机器人弹跳的固定距离,以及希望至少获得的分数。相邻两个数 之间用一个空格隔开。 接下来 n� 行,每行两个整数 xi,si��,�� ,分别表示起点到第 i� 个格子的距离以及第 i� 个格子的分数。两个数之间用一个空格隔开。保证 xi�� 按递增顺序输入。
输出
共一行,一个整数,表示至少要花多少金币来改造他的机器人。若无论如何他都无法获得至少 k� 分,输出 −1−1 。
样例输入 复制
7 4 10 2 6 5 -3 10 3 11 -3 13 1 17 6 20 2
样例输出 复制
2
提示
输入样例 2
7 4 20 2 6 5 -3 10 3 11 -3 13 1 17 6 20 2
输出样例 2
-1
【输入输出样例 1 说明】
22个金币改进后, 小 R 的机器人依次选择的向右弹跳的距离分别为2,3,5,3,4,32,3,5,3,4,3, 先后到达的位置分别为 2,5,10,13,17,202,5,10,13,17,20, 对应1,2,3,5,6,71,2,3,5,6,7 这66 个格子。这些格子中的数字之和1515 即为小 R 获得的分数。
【输入输出样例 2 说明】
由于样例中 77 个格子组合的最大可能数字之和只有 1818 ,无论如何都无法获得2020分
数据规模与约定
本题共 10 组测试数据,每组数据 10 分。
对于全部的数据满足1≤n≤500000,1≤d≤2000,1≤xi,k≤109,|si|<1051≤�≤500000,1≤�≤2000,1≤��,�≤109,|��|<105。
对于第 1,21,2组测试数据, n≤10�≤10;
对于第3,4,53,4,5 组测试数据, n≤500�≤500
对于第6,7,86,7,8 组测试数据, d=1
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL inf=1LL<<34;
const int N=500000;
int n,d,x[N+3];
LL k,s[N+3],f[N+3];
bool chk(int g){
for(int i=1;i<=n;i++)
f[i]=-inf;
f[0]=0;
for(int i=0;i<=n;i++)
if(f[i]!=-inf){
int j;
for(j=i+1;x[j]<x[i]+(d-g)&&j<=n
&&x[j]<=x[i]+(d+g);j++);
for(;x[j]<=x[i]+(d+g)&&j<=n;j++)
f[j]=max(f[j],f[i]+s[j]);
}
LL tmp=-inf;
for(int i=1;i<=n;i++)
tmp=max(tmp,f[i]);
return tmp>=k;
}
int main(){
scanf("%d%d%lld",&n,&d,&k);
for(int i=1;i<=n;i++)
scanf("%d%lld",&x[i],&s[i]);
x[0]=0;
int l=0,r=x[n]-d,mid,ans=-1;
while(l<=r){
mid=(l+r)>>1;
if(chk(mid)){
ans=mid;
r=mid-1;
}
else
l=mid+1;
}
printf("%d",ans);
return 0;
}
2197: 对称二叉树(NOIP 2018 PJT4)
题目描述
一棵有点权的有根树如果满足以下条件,则被轩轩称为对称二叉树:
1. 二叉树; 2. 将这棵树所有节点的左右子树交换,新树和原树对应位置的结构相同且点权相等。
下图中节点内的数字为权值,节点外的 id�� 表示节点编号。
现在给出一棵二叉树,希望你找出它的一棵子树,该子树为对称二叉树,且节点数 最多。请输出这棵子树的节点数。
注意:只有树根的树也是对称二叉树。本题中约定,以节点 T� 为子树根的一棵“子 树”指的是:节点T� 和它的全部后代节点构成的二叉树。
输入
第一行一个正整数 n�,表示给定的树的节点的数目,规定节点编号 1∼n1∼�,其中节点 11 是树根。 第二行 n� 个正整数,用一个空格分隔,第 i� 个正整数 vi�� 代表节点 i� 的权值。 接下来 n� 行,每行两个正整数 li,ri��,��,分别表示节点 i� 的左右孩子的编号。如果不存在左 / 右孩子,则以 −1−1 表示。两个数之间用一个空格隔开。
输出
输出文件共一行,包含一个整数,表示给定的树的最大对称二叉子树的节点数。
样例输入 复制
2 1 3 2 -1 -1 -1
样例输出 复制
1
提示
输入样例 #2
10 2 2 5 5 5 5 4 4 2 3 9 10 -1 -1 -1 -1 -1 -1 -1 -1 -1 2 3 4 5 6 -1 -1 7 8
输出样例 #2
3
【输入输出样例 1 说明】
最大的对称二叉子树为以节点 22 为树根的子树,节点数为 11。
【输入输出样例 2 说明】
最大的对称二叉子树为以节点 77 为树根的子树,节点数为 33。
【数据规模与约定】
共 2525 个测试点。
vi≤1000��≤1000。
测试点 1∼3,n≤101∼3,�≤10,保证根结点的左子树的所有节点都没有右孩子,根结点的右 子树的所有节点都没有左孩子。
测试点 4∼8,n≤104∼8,�≤10。
测试点 9∼12,n≤1059∼12,�≤105,保证输入是一棵“满二叉树” 。
测试点 13∼16,n≤10513∼16,�≤105,保证输入是一棵“完全二叉树”。
测试点 17∼20,n≤10517∼20,�≤105,保证输入的树的点权均为 11。
测试点 21∼25,n≤10621∼25,�≤106。
#include<bits/stdc++.h>
using namespace std;
int w[1000005];
int a[1000005][2];
int s[1000005];
void dfs(int x)//求出以编号x的节点为根的树的节点数量
{
s[x]=1;//根
if(a[x][0]!=-1) dfs(a[x][0]),s[x]+=s[a[x][0]];//左子树
if(a[x][1]!=-1) dfs(a[x][1]),s[x]+=s[a[x][1]];//右子树
}
bool dfs1(int x,int y)
{
if(x==-1&&y==-1) return 1;//无左右子树,即叶节点,对称
if(x!=-1&&y!=-1&&s[x]==s[y]&&w[x]==w[y])//左右相应位置比较
{
if(dfs1(a[x][0],a[y][1])&&dfs1(a[x][1],a[y][0])) return 1;//左左-右右,左右-右左
}
return 0;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);//输入每个节点的权值
for(int i=1;i<=n;i++) scanf("%d%d",&a[i][0],&a[i][1]);//输入左右孩子
dfs(1);
int ans=0;
for(int i=1;i<=n;i++)
{
if(dfs1(a[i][0],a[i][1])) ans=max(ans,s[i]);//求出所有对称二叉树中节点最多的
}
printf("%d",ans);
return 0;
}
2201: 加工零件(CSP-J 2019 T4)
题目描述
凯凯的工厂正在有条不紊地生产一种神奇的零件,神奇的零件的生产过程自然也很神奇。工厂里有 n� 位工人,工人们从 1∼n1∼� 编号。某些工人之间存在双向的零件传送带。保证每两名工人之间最多只存在一条传送带。
如果 x� 号工人想生产一个被加工到第 L(L>1)�(�>1) 阶段的零件,则所有与 x� 号工人有传送带直接相连的工人,都需要生产一个被加工到第 L−1�−1 阶段的零件(但 x� 号工人自己无需生产第 L−1�−1 阶段的零件)。
如果 x� 号工人想生产一个被加工到第 1 阶段的零件,则所有与 x� 号工人有传送带直接相连的工人,都需要为 x� 号工人提供一个原材料。
轩轩是 1 号工人。现在给出 q� 张工单,第 i� 张工单表示编号为 ai�� 的工人想生产一个第 Li�� 阶段的零件。轩轩想知道对于每张工单,他是否需要给别人提供原材料。他知道聪明的你一定可以帮他计算出来!
输入
第一行三个正整数 n�,m� 和 q�,分别表示工人的数目、传送带的数目和工单的数目。 接下来 m� 行,每行两个正整数 u� 和 v�,表示编号为 u� 和 v� 的工人之间存在一条零件传输带。保证 u≠v�≠�。 接下来 q� 行,每行两个正整数 a� 和 L�,表示编号为 a� 的工人想生产一个第 L� 阶段的零件。
输出
共 q� 行,每行一个字符串 `Yes` 或者 `No`。如果按照第 i� 张工单生产,需要编号为 1 的轩轩提供原材料,则在第 i� 行输出 `Yes`;否则在第 i� 行输出 `No`。注意输出**不含**引号。
样例输入 复制
3 2 6 1 2 2 3 1 1 2 1 3 1 1 2 2 2 3 2
样例输出 复制
No Yes No Yes No Yes
提示
输入样例 #2
5 5 5 1 2 2 3 3 4 4 5 1 5 1 1 1 2 1 3 1 4 1 5
输出样例 #2
No Yes No Yes Yes
【数据规模与约定】
共 20 个测试点。
1≤u,v,a≤n1≤�,�,�≤�。
测试点 1~4,1≤n,m≤10001≤�,�≤1000,q=3�=3,L=1�=1。
测试点 5~8,1≤n,m≤10001≤�,�≤1000,q=3�=3,1≤L≤101≤�≤10。
测试点 9~12,1≤n,m,L≤10001≤�,�,�≤1000,1≤q≤1001≤�≤100。
测试点 13~16,1≤n,m,L≤10001≤�,�,�≤1000,1≤q≤1051≤�≤105。
测试点 17~20,1≤n,m,q≤1051≤�,�,�≤105,1≤L≤1091≤�≤109。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int h[N], e[N * 2], ne[N * 2], idx;
int n, m, q;
int dist[N][2];
bool st[N][2];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void bfs()
{
for (int i = 1; i <= n; i++) dist[i][0] = dist[i][1] = 2e9;
dist[1][0] = 0;
queue<int> q;
q.push(1);
while (!q.empty())
{
int fr = q.front(); q.pop();
for (int i = h[fr]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j][1] > dist[fr][0] + 1)
{
dist[j][1] = dist[fr][0] + 1;
if (!st[j][1])
{
st[j][1] = true;
q.push(j);
}
}
if (dist[j][0] > dist[fr][1] + 1)
{
dist[j][0] = dist[fr][1] + 1;
if (!st[j][0])
{
st[j][0] = true;
q.push(j);
}
}
}
}
}
int main()
{
scanf("%d%d%d", &n, &m, &q);
memset(h, -1, sizeof h);
while (m--)
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b); add(b, a);
}
bfs();
if (h[1] == -1) dist[1][0] = 2e9;
while (q--)
{
int a, L;
scanf("%d%d", &a, &L);
if (L >= dist[a][L % 2]) printf("Yes\n");
else printf("No\n");
}
return 0;
}
2208: 侦探推理(NOIP 2003 TGT2)
题目描述
明明同学最近迷上了侦探漫画《柯南》并沉醉于推理游戏之中,于是他召集了一群同学玩推理游戏。游戏的内容是这样的,明明的同学们先商量好由其中的一个人充当罪犯(在明明不知情的情况下),明明的任务就是找出这个罪犯。接着,明明逐个询问每一个同学,被询问者可能会说:
证词中出现的其他话,都不列入逻辑推理的内容。
明明所知道的是,他的同学中有N个人始终说假话,其余的人始终说真。
现在,明明需要你帮助他从他同学的话中推断出谁是真正的凶手,请记住,凶手只有一个!
输入
输入由若干行组成,第一行有三个整数,M(1≤M≤20)�(1≤�≤20)、N(1≤N≤M)�(1≤�≤�)和P(1≤P≤100)�(1≤�≤100);M�是参加游戏的明明的同学数,N�是其中始终说谎的人数,P�是证言的总数。 接下来M�行,每行是明明的一个同学的名字(英文字母组成,没有空格,全部大写)。 往后有P�行,每行开始是某个同学的名宇,紧跟着一个冒号和一个空格,后面是一句证词,符合前表中所列格式。证词每行不会超过250250个字符。 输入中不会出现连续的两个空格,而且每行开头和结尾也没有空格。
输出
如果你的程序能确定谁是罪犯,则输出他的名字;如果程序判断出不止一个人可能是罪犯,则输出 "Cannot Determine";如果程序判断出没有人可能成为罪犯,则输出 "Impossible"。
样例输入 复制
3 1 5 MIKE CHARLES KATE MIKE: I am guilty. MIKE: Today is Sunday. CHARLES: MIKE is guilty. KATE: I am guilty. KATE: How are you??
样例输出 复制
MIKE
#include<bits/stdc++.h>
#define debug cout
using namespace std;
const int maxn=30;
string name[maxn];
int ptr[maxn][maxn]; // 1 means is , -1 means not;
int day[maxn]; // means says day.
int day_can_be[maxn]; // can be that day ? 1 means is , -1 means is not.
int gul_can_be[maxn]; // persob i can be guilty or not , 1 means is , -1 means is not;
int may_be_ans[maxn]; // 0,1 means can be answer or not
string buf[1<<10];
map<string,int> person;
int n,m,p,mx,cnt,ans;
inline int getdate(string x)
{
if(x=="monday.") return 1;
if(x=="tuesday.") return 2;
if(x=="wednesday.") return 3;
if(x=="thursday.") return 4;
if(x=="friday.") return 5;
if(x=="saturday.") return 6;
if(x=="sunday.") return 7;
return puts("Wrong spelling ! Fuck you!"),-1;
}
inline char convchar(char x)
{
if( x>='A' && x<='Z' )
return x-'A'+'a';
else return x;
}
inline void convstring(string &x)
{
for(unsigned i=0;i<x.length();i++)
x[i] = convchar(x[i]);
}
inline char nextchar(int arg=0)
{
static char buf[1<<10],*st;
if(arg)
{
fgets(buf,1<<10,stdin),st=buf;
int i;
for(i=0;i<1<<10&&buf[i];i++)
if( buf[i]=='\r' )
buf[i] = '\n';
buf[i] = '\n';
}
return *st++;
}
inline void getline()
{
cnt = 0;
char c=nextchar(1);
cnt = 1;
while( c != '\n' )
{
if(c==' ')
{
if( person.find(buf[cnt]) == person.end() )
convstring(buf[cnt]);
cnt++;
}
else
buf[cnt] = buf[cnt] + c;
c = nextchar();
}
while( buf[cnt]=="" ) cnt--;
convstring(buf[cnt]);
}
inline void resbuf()
{
for(int i=1;i<=cnt;i++)
buf[i].clear(),
buf[i].resize(0);
}
inline void explain()
{
if( buf[2]!="i" && buf[2]!="today" && person.find(buf[2])==person.end() ) return;
const int id = person[buf[1]];
if( buf[2]=="today" )
{
int dd = getdate(buf[4]);
if( day[id] && day[id]!=dd )
{
ans = -2;
return;
}
day[id] = dd;
}
else if( buf[4]=="guilty." )
{
int tar = buf[2]=="i" ? id : person[buf[2]];
if( ptr[id][tar] && ptr[id][tar]!=1 )
{
ans = -2;
return;
}
ptr[id][tar] = 1;
}
else if( buf[5]=="guilty." )
{
int tar = buf[2]=="i" ? id : person[buf[2]];
if( ptr[id][tar] && ptr[id][tar]!=-1 )
{
ans = -2;
return;
}
ptr[id][tar] = -1;
}
}
inline void reslogic()
{
memset(day_can_be,0,sizeof(day_can_be));
memset(gul_can_be,0,sizeof(gul_can_be));
}
inline void logic(int x,int mul,int& flag)
{
if(day[x])
{
if( day_can_be[day[x]] && day_can_be[day[x]]!=mul )
{
flag=0;
return;
}
day_can_be[day[x]] = mul;
}
for(int i=1;i<=n;i++)
if(ptr[x][i])
{
const int pp = ptr[x][i]*mul;
if( gul_can_be[i] && gul_can_be[i]!=pp )
{
flag = 0;
return;
}
gul_can_be[i] = pp;
}
}
inline bool judgedate()
{
int ret=0;
for(int i=1;i<=7;i++)
if( ~day_can_be[i] )
ret += day_can_be[i];
return ret<2;
}
inline void judgegul()
{
int pos = -1,siz=n;
for(int i=1;i<=n;i++)
{
if( !~gul_can_be[i] ) --siz;
else if( gul_can_be[i] == 1 )
{
pos = i;
break;
}
}
if( siz>1 && !~pos ) // can not determine , maybe multi guilty .
{
ans = -1;
return;
}
if( siz==1 && !~pos )
for(int i=1;i<=n;i++)
if( !gul_can_be[i] )
{
may_be_ans[i] = 1;
return;
}
for(int i=1;i<=n;i++)
if( i!=pos && gul_can_be[i] == 1 ) // must be multi guilty in this statement
return;
may_be_ans[pos] = 1;
}
inline int count(int x)
{
int ret=0;
while(x)
ret++,
x -= (x&-x);
return ret;
}
inline int getans()
{
int ret = 0;
for(int i=1;i<=n;i++)
if( may_be_ans[i] )
ret++,
ans = i;
return ret;
}
int main()
{
scanf("%d%d%d",&n,&m,&p);
mx = (1<<n);
for(int i=1;i<=n;i++)
{
cin>>name[i];
person[name[i]] = person[name[i]+":"] = i;
}
char c = nextchar(1);
while(c!='\n') c=nextchar();
for(int i=1;i<=p;i++)
{
resbuf();
getline();
explain();
}
if( ans == -2 )
return puts("Impossible"),0;
for(int s=0;s<mx;s++)
{
if( count(s) != m ) continue;
reslogic();
int flag =1;
for(int i=1;i<=n&&flag;i++)
if( s & (1<<(i-1)) )
logic(i,-1,flag);
else logic(i,1,flag);
if( flag && judgedate() )
judgegul();
}
if( !~ans )
return puts("Cannot Determine"),0;
if( !getans() )
return puts("Impossible"),0;
if( getans() > 1 )
return puts("Cannot Determine"),0;
cout<<name[ans]<<endl;
return 0;
}
2212: 合并果子(NOIP 2004 TGT2)
题目描述
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n−1�−1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。 因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 11 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有 33 种果子,数目依次为 11 , 22 , 99 。可以先将 11 、 22 堆合并,新堆数目为 33 ,耗费体力为 33 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 1212 ,耗费体力为 1212 。所以多多总共耗费体力 =3+12=15=3+12=15 。可以证明 1515 为最小的体力耗费值。
输入
共两行。 第一行是一个整数 n(1≤n≤10000)�(1≤�≤10000) ,表示果子的种类数。 第二行包含 n� 个整数,用空格分隔,第 i� 个整数 ai(1≤ai≤20000)��(1≤��≤20000) 是第 i� 种果子的数目。
输出
一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 231231 。
样例输入 复制
3 1 2 9
样例输出 复制
15
提示
对于 30%30% 的数据,保证有 n≤1000�≤1000:
对于 50%50% 的数据,保证有 n≤5000�≤5000;
对于全部的数据,保证有 n≤10000�≤10000。
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
int ans;
priority_queue <int,vector<int>,greater<int> >apple;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int a;
scanf("%d",&a);
apple.push(a);
}
while(apple.size()>1)
{
int x=apple.top();apple.pop();
int y=apple.top();apple.pop();
apple.push(x+y);
ans=ans+x+y;
}
printf("%d",ans);
return 0;
}
2226: 过河(NOIP 2005 TGT2)
题目描述
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,⋯,L0,1,⋯,�(其中 L� 是桥的长度)。坐标为 00 的点表示桥的起点,坐标为 L� 的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是 S� 到 T� 之间的任意正整数(包括 S,T�,�)。当青蛙跳到或跳过坐标为 L� 的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度 L�,青蛙跳跃的距离范围 S,T�,�,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
输入
第一行有 11 个正整数 L(1≤L≤109)�(1≤�≤109),表示独木桥的长度。 第二行有 33 个正整数 S,T,M�,�,�,分别表示青蛙一次跳跃的最小距离,最大距离及桥上石子的个数,其中 1≤S≤T≤101≤�≤�≤10,1≤M≤1001≤�≤100。 第三行有 M� 个不同的正整数分别表示这 M� 个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。
输出
一个整数,表示青蛙过河最少需要踩到的石子数。
样例输入 复制
10 2 3 5 2 3 5 6 7
样例输出 复制
2
提示
对于30%的数据,L≤104�≤104;
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll MAXN=300 * 105;
ll read(){
ll ans = 0, f = 1;
char ch=getchar();
while(!isdigit(ch))
f*=(ch=='-')?-1:1,ch=getchar();
do ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar();
while(isdigit(ch));
return ans*f;
}
ll L;
ll S,T,M;
int Nx[MAXN],a[MAXN];
int vis[MAXN],dp[MAXN];
int all;
int main(){
L=read();
S=read();
T=read();
M=read();
int lcm=S*T;
for(int i=1;i<=M;i++){
Nx[i]=read();
}
sort(Nx+1,Nx+M+1);
if(S==T){
int cnt=0;
for(int i=1;i<=M;i++)
if(Nx[i]%S==0)all++;
cout<<all<<endl;
return 0;
}
for(ll i=1;i<=M;i++){
int len=Nx[i]-Nx[i-1];
if(len>=lcm)len=lcm;
a[i]=a[i-1]+len;
vis[a[i]]=1;
}
L=a[M]+lcm;
memset(dp,0x7f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=L;i++)
for(int j=S;j<=T;j++)
{
if(i-j>=0)if(vis[i])dp[i] = min(dp[i - j] + 1,dp[i]);
else dp[i] = min(dp[i - j],dp[i]);
}
int ans=0x7f;
for(int i=a[M];i<=L;++i)ans=min(ans,dp[i]);
cout << ans << endl;
return 0;
}
2249: 金明的预算方案(NOIP 2006 TGT2)
题目描述
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 n� 元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 00 个、11 个或 22 个附件。每个附件对应一个主件,附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的 n� 元。于是,他把每件物品规定了一个重要度,分为 55 等:用整数 1∼51∼5 表示,第 55 等最重要。他还从因特网上查到了每件物品的价格(都是 1010 元的整数倍)。他希望在不超过 n� 元的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第 j� 件物品的价格为 vj��,重要度为wj��,共选中了 k� 件物品,编号依次为 j1,j2,…,jk�1,�2,…,��,则所求的总和为:
vj1×wj1+vj2×wj2+⋯+vjk×wjk��1×��1+��2×��2+⋯+���×���。
请你帮助金明设计一个满足要求的购物单。
输入
第一行有两个整数,分别表示总钱数 n� 和希望购买的物品个数 m�。 第 22 到第 (m+1)(�+1) 行,每行三个整数,第 (i+1)(�+1) 行的整数 vi��,pi��,qi�� 分别表示第 i� 件物品的价格、重要度以及它对应的的主件。如果 qi=0��=0,表示该物品本身是主件。
输出
输出一行一个整数表示答案。
样例输入 复制
1000 5 800 2 0 400 5 1 300 5 1 400 3 0 500 2 0
样例输出 复制
2200
提示
数据规模与约定
对于全部的测试点,保证 1≤n≤3.2×1041≤�≤3.2×104,1≤m≤601≤�≤60,0≤vi≤1040≤��≤104,1≤pi≤51≤��≤5,0≤qi≤m0≤��≤�,答案不超过 2×1052×105。
#include <stdio.h>
#include <stdlib.h>
#define maxn 32060
int N,m,v,p,q;
int main_item_value[maxn],main_item_weight[maxn];
int minor_item_value[maxn][3],minor_item_weight[maxn][3];
int f[maxn];
int max(int a,int b)
{
return a>b?a:b;
}
int main()
{
scanf("%d%d",&N,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&v,&p,&q);
if(!q)
{
main_item_value[i]=v;
main_item_weight[i]=v*p;
}
else
{
minor_item_value[q][0]++;
minor_item_value[q][minor_item_value[q][0]]=v;
minor_item_weight[q][minor_item_value[q][0]]=v*p;
}
}
for(int i=1;i<=m;i++)
{
for(int j=N;j>=0;j--)
{
if(j>=main_item_value[i])
{
f[j]=max(f[j],f[j-main_item_value[i]]+main_item_weight[i]);
if(j>=main_item_value[i]+minor_item_value[i][1])
{
f[j]=max(f[j],f[j-main_item_value[i]-minor_item_value[i][1]]+main_item_weight[i]+minor_item_weight[i][1]);
}
if(j>=main_item_value[i]+minor_item_value[i][2])
{
f[j]=max(f[j],f[j-main_item_value[i]-minor_item_value[i][2]]+main_item_weight[i]+minor_item_weight[i][2]);
}
if(j>=main_item_value[i]+minor_item_value[i][1]+minor_item_value[i][2])
{
f[j]=max(f[j],f[j-main_item_value[i]-minor_item_value[i][2]-minor_item_value[i][1]]+main_item_weight[i]+minor_item_weight[i][2]+minor_item_weight[i][1]);
}
}
}
}
printf("%d",f[N]);
return 0;
}
2271: 字符串的展开(NOIP 2007 TGT2)
题目描述
在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于“`d-h`”或者“`4-8`”的字串,我们就把它当作一种简写,输出时,用连续递增的字母或数字串替代其中的减号,即,将上面两个子串分别输出为“`defgh`”和“`45678`"。在本题中,我们通过增加一些参数的设置,使字符串的展开更为灵活。具体约定如下:
(1) 遇到下面的情况需要做字符串的展开:在输入的字符串中,出现了减号“`-`”,减号两侧同为小写字母或同为数字,且按照`ASCII`码的顺序,减号右边的字符严格大于左边的字符。
(2) 参数p1�1:展开方式。p1=1�1=1时,对于字母子串,填充小写字母;p1=2�1=2时,对于字母子串,填充大写字母。这两种情况下数字子串的填充方式相同。p1=3�1=3时,不论是字母子串还是数字字串,都用与要填充的字母个数相同的星号“\*”来填充。
(3) 参数p2�2:填充字符的重复个数。p2=k�2=�表示同一个字符要连续填充k个。例如,当p2=3�2=3时,子串“`d-h`”应扩展为“`deeefffgggh`”。减号两边的字符不变。
(4) 参数p3�3:是否改为逆序:p3=1�3=1表示维持原来顺序,p3=2�3=2表示采用逆序输出,注意这时候仍然不包括减号两端的字符。例如当p1=1�1=1、p2=2�2=2、p3=2�3=2时,子串“`d-h`”应扩展为“`dggffeeh`”。
(5) 如果减号右边的字符恰好是左边字符的后继,只删除中间的减号,例如:“`d-e`”应输出为“`de`”,“`3-4`”应输出为“`34`”。如果减号右边的字符按照`ASCII`码的顺序小于或等于左边字符,输出时,要保留中间的减号,例如:“`d-d`”应输出为“`d-d`”,“`3-1`”应输出为“`3-1`”。
输入
共两行。 第11行为用空格隔开的33个正整数,依次表示参数p1,p2,p3�1,�2,�3。 第22行为一行字符串,仅由数字、小写字母和减号“−−”组成。行首和行末均无空格。
输出
共一行,为展开后的字符串。
样例输入 复制
1 2 1 abcs-w1234-9s-4zz
样例输出 复制
abcsttuuvvw1234556677889s-4zz
提示
输入样例 #2
2 3 2
a-d-d
输出样例 #2
aCCCBBBd-d
40%40%的数据满足:字符串长度不超过55
100%100%的数据满足:1≤p1≤3,1≤p2≤8,1≤p3≤21≤�1≤3,1≤�2≤8,1≤�3≤2。字符串长度不超过100
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
long long read(){
long long ans = 0, f = 1;
char ch = getchar();
while(!isdigit(ch))
f *= (ch == '-') ? -1 : 1, ch = getchar();
do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
while(isdigit(ch));
return ans * f;
}
inline bool isValid(char a,char b){
if(a >= b) return false;
if(isdigit(a) && isdigit(b)) return true;
if((a >= 'a' && a <= 'z') && (b >= 'a' && b <= 'z')) return true;
return false;
}
int main(){
// freopen("expand.in", "r", stdin);
// freopen("expand.out", "w", stdout);
int p1 = read(), p2 = read(), p3 = read();
string s;
cin >> s;
int len = s.length();
for(int i=0; i<len; i++){
if(s[i] == '-' && i != 0 && i != len-1 && isValid(s[i-1], s[i+1])){
int add = (!isdigit(s[i-1]) && (p1 == 2)) * ('A' - 'a');
char begin = s[i-1], end = s[i+1];
if(p3 == 2) swap(begin, end);
int d = (begin < end) ? 1 : -1;
for(char k=begin; k!=end; k+=d){
if(k == begin) continue;
for(int j=1; j<=p2; j++){
if(p1 == 3) putchar('*');
else putchar(k+add);
}
}
}
else putchar(s[i]);
}
return 0;
}
2275: 火柴棒等式(NOIP 2008 TGT2)
题目描述
给你n根火柴棍,你可以拼出多少个形如“A+B=C�+�=�”的等式?等式中的A�、B�、C�是用火柴棍拼出的整数(若该数非零,则最高位不能是00)。用火柴棍拼数字0−90−9的拼法如图所示:
注意:
1. 加号与等号各自需要两根火柴棍
2. 如果A≠B�≠�,则A+B=C�+�=�与B+A=C�+�=�视为不同的等式(A,B,C>=0�,�,�>=0)
3. n�根火柴棍必须全部用上
输入
一个整数n(n<=24)�(�<=24)。
输出
一个整数,能拼成的不同等式的数目。
样例输入 复制
14
样例输出 复制
2
提示
3>输入样例 #2
18
输出样例 #2
9
【输入输出样例1解释】
22个等式为0+1=10+1=1和1+0=11+0=1。
【输入输出样例2解释】
99个等式为:
0+4=4
0+11=11
1+10=11
2+2=4
2+7=9
4+0=4
7+2=9
10+1=11
11+0=11
#include<iostream>
using namespace std;
int n, sum;
int match[10] = { 6,2,5,5,4,5,6,3,7,6 };
int match_of(int n)
{
int sum = 0, m = n;
if (0 == n)
return 6;
else
{
for (; 0 != m; m /= 10)
sum += match[m % 10];
return sum;
}
}
int main()
{
cin >> n;
if (n <= 10)
cout << '0';
else
{
n -= 4;
for (int i = 0; i <= 1111; i++)
for (int j = 0; j <= i; j++)
if (n == match_of(j) + match_of(i - j) + match_of(i))
sum++;
cout << sum;
}
return 0;
}