T1:
题目描述
参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 nn 个深埋在地下的宝藏屋, 也给出了这 nn 个宝藏屋之间可供开发的mm 条道路和它们的长度。
小明决心亲自前往挖掘所有宝藏屋中的宝藏。但是,每个宝藏屋距离地面都很远, 也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路 则相对容易很多。
小明的决心感动了考古挖掘的赞助商,赞助商决定免费赞助他打通一条从地面到某 个宝藏屋的通道,通往哪个宝藏屋则由小明来决定。
在此基础上,小明还需要考虑如何开凿宝藏屋之间的道路。已经开凿出的道路可以 任意通行不消耗代价。每开凿出一条新道路,小明就会与考古队一起挖掘出由该条道路 所能到达的宝藏屋的宝藏。另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏 屋之间的道路无需再开发。
新开发一条道路的代价是:
\mathrm{L} \times \mathrm{K}L×K
L代表这条道路的长度,K代表从赞助商帮你打通的宝藏屋到这条道路起点的宝藏屋所经过的 宝藏屋的数量(包括赞助商帮你打通的宝藏屋和这条道路起点的宝藏屋) 。
请你编写程序为小明选定由赞助商打通的宝藏屋和之后开凿的道路,使得工程总代 价最小,并输出这个最小值。
输入格式
第一行两个用空格分离的正整数 n,mn,m,代表宝藏屋的个数和道路数。
接下来 mm 行,每行三个用空格分离的正整数,分别是由一条道路连接的两个宝藏 屋的编号(编号为 1-n1−n),和这条道路的长度 vv。
输出格式
一个正整数,表示最小的总代价。
输入输出样例
输入 #1复制
4 5 1 2 1 1 3 3 1 4 1 2 3 4 3 4 1
输出 #1复制
4
输入 #2复制
4 5 1 2 1 1 3 3 1 4 1 2 3 4 3 4 2
输出 #2复制
5
说明/提示
【样例解释1】
小明选定让赞助商打通了11 号宝藏屋。小明开发了道路 1 \to 21→2,挖掘了 22 号宝 藏。开发了道路 1 \to 41→4,挖掘了 44 号宝藏。还开发了道路 4 \to 34→3,挖掘了33号宝 藏。工程总代价为:1 \times 1 + 1 \times 1 + 1 \times 2 = 41×1+1×1+1×2=4
【样例解释2】
小明选定让赞助商打通了11 号宝藏屋。小明开发了道路 1 \to 21→2,挖掘了 22 号宝 藏。开发了道路 1 \to 31→3,挖掘了 33 号宝藏。还开发了道路 1 \to 41→4,挖掘了44号宝 藏。工程总代价为:1 \times 1 + 3 \times 1 + 1 \times 1 = 51×1+3×1+1×1=5
【数据规模与约定】
对于20\%20%的数据: 保证输入是一棵树,1 \le n \le 81≤n≤8,v \le 5000v≤5000 且所有的 vv都相等。
对于 40\%40%的数据: 1 \le n \le 81≤n≤8,0 \le m \le 10000≤m≤1000,v \le 5000v≤5000 且所有的vv都相等。
对于70\%70%的数据: 1 \le n \le 81≤n≤8,0 \le m \le 10000≤m≤1000,v \le 5000v≤5000
对于100\%100%的数据: 1 \le n \le 121≤n≤12,0 \le m \le 10000≤m≤1000,v \le 500000v≤500000
#include<bits/stdc++.h>
using namespace std;
int n,m,c[20][20],vis[20],lev[20],ans=1e9,u,v,w,tmp,tot,cnt,p,tar[20][20],d[20];
inline bool cmp(int a, int b) {
return c[p][a] < c[p][b];
}
inline void dfs(int num,int node){
for(int i = num; i <= cnt; i ++) {
if(tot + tmp * lev[vis[i]] >= ans) return;
for(int j = node; j <= d[vis[i]]; j ++)
if(!lev[tar[vis[i]][j]]){
cnt++;
vis[cnt]=tar[vis[i]][j];
tmp -= c[vis[cnt]][tar[vis[cnt]][1]];
tot+=c[vis[i]][vis[cnt]]*lev[vis[i]];
lev[vis[cnt]]=lev[vis[i]]+1;
dfs(i,j+1);
tot-=c[vis[i]][vis[cnt]]*lev[vis[i]];
lev[vis[cnt]]=0;
tmp += c[vis[cnt]][tar[vis[cnt]][1]];
cnt--;
}
node=1;
}
if(cnt == n) {
if(tot < ans) {
ans = tot;
}
return;
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
c[i][j]=1e9;
for(int i=1;i<=m;i++){
cin >>u>>v>>w;
if(w>c[u][v]){
continue;
}
if(c[u][v]==1e9){
tar[u][++d[u]]=v;
tar[v][++d[v]]=u;
}
c[u][v]=c[v][u]=w;
}
for(int i=1;i<=n;i++){
p=i;
sort(tar[i]+1,tar[i]+1+d[i],cmp);
tmp += c[i][tar[i][1]];
}
for(int i=1;i<=n;i++){
tot=0;
cnt=1;
vis[1]=i;
lev[i]=1;
tmp -= c[i][tar[i][1]];
dfs(1,1);
lev[i]=0;
tmp += c[i][tar[i][1]];
}
cout<<ans;
return 0;
}
//vis : 已经访问过的点
//lev : 点距离初始点的距离
//c : 费用
//tar :存下每个点能到的点
//ans : 最终的答案
//tmp : 最优解剪枝用
//tot : dfs储存中间答案的用具
//cnt :现在已经访问过的点数
//n : 点数
//m : 边数
//p : sort用品
T2:
题目描述
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n \times mn×m的矩阵,矩阵中的每个元素a_{i,j}ai,j均为非负整数。游戏规则如下:
- 每次取数时须从每行各取走一个元素,共nn个。经过mm次后取完矩阵内所有元素;
- 每次取走的各个元素只能是该元素所在行的行首或行尾;
- 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值\times 2^i×2i,其中ii表示第ii次取数(从11开始编号);
- 游戏结束总得分为mm次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。
输入格式
输入文件包括n+1n+1行:
第11行为两个用空格隔开的整数nn和mm。
第2\backsim n+12∽n+1行为n \times mn×m矩阵,其中每行有mm个用单个空格隔开的非负整数。
输出格式
输出文件仅包含11行,为一个整数,即输入矩阵取数后的最大得分。
输入输出样例
输入 #1复制
2 3 1 2 3 3 4 2
输出 #1复制
82
说明/提示
NOIP 2007 提高第三题
数据范围:
60%的数据满足:1\le n, m \le 301≤n,m≤30,答案不超过10^{16}1016
100%的数据满足:1\le n, m \le 801≤n,m≤80,0 \le a_{i,j} \le 10000≤ai,j≤1000
#include<cstdio>
struct int128
{
long long hig;
long long low;
};
int n,m;
long long p=1e18;
int128 ans,f[85][85][85],a[85][85];
int128 max(int128 a,int128 b)
{
if(a.hig>b.hig) return a;
if(a.hig<b.hig) return b;
if(a.low>b.low) return a;
if(a.low<b.low) return b;
return a;
}
int128 operator + (int128 a,int128 b)
{
int128 k;
k.low=0,k.hig=0;
k.low=a.low+b.low;
k.hig=k.low/p+a.hig+b.hig;
k.low%=p;
return k;
}
int128 operator * (int128 a,int b)
{
int128 k;
k.low=0,k.hig=0;
k.low=a.low*b;
k.hig+=k.low/p+b*a.hig;
k.low%=p;
return k;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%lld",&a[i][j].low);
for(int i=1;i<=n;i++)
for(int len=0;len<m;len++)
for(int l=1;l+len<=m;l++)
f[l][l+len][i]=max(f[l+1][l+len][i]+a[i][l],f[l][l+len-1][i]+a[i][l+len])*2;
for(int i=1;i<=n;i++)
ans=ans+f[1][m][i];
if(ans.hig==0) printf("%lld",ans.low);
else printf("%lld%018lld\n",ans.hig,ans.low);
}
T3:
题目描述
小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序, 于是你的机会来啦!下面请你编写程序来判断小明对他的每个程序给出的时间复杂度是否正确。
A++语言的循环结构如下:
F i x y
循环体
E
其中F i x y
表示新建变量 ii(变量 ii 不可与未被销毁的变量重名)并初始化为 xx, 然后判断 ii 和 yy 的大小关系,若 ii 小于等于 yy 则进入循环,否则不进入。每次循环结束后 ii 都会被修改成 i +1i+1,一旦 ii 大于 yy终止循环。
xx 和 yy 可以是正整数(xx 和 yy 的大小关系不定)或变量 nn。nn 是一个表示数据规模的变量,在时间复杂度计算中需保留该变量而不能将其视为常数,该数远大于 100100。
“E”表示循环体结束。循环体结束时,这个循环体新建的变量也被销毁。
注:本题中为了书写方便,在描述复杂度时,使用大写英文字母“O”表示通常意义下“Θ”的概念。
输入格式
输入文件第一行一个正整数 tt,表示有 tt(t \le 10t≤10)个程序需要计算时间复杂度。 每个程序我们只需抽取其中 F i x y
和E
即可计算时间复杂度。注意:循环结构 允许嵌套。
接下来每个程序的第一行包含一个正整数 LL 和一个字符串,LL 代表程序行数,字符 串表示这个程序的复杂度,O(1)
表示常数复杂度,O(n^w)
表示复杂度为n^wnw,其 中w是一个小于100的正整数(输入中不包含引号),输入保证复杂度只有O(1)
和O(n^w)
两种类型。
接下来 LL 行代表程序中循环结构中的F i x y
或者 E
。 程序行若以F
开头,表示进入一个循环,之后有空格分离的三个字符(串)i x y
, 其中 ii 是一个小写字母(保证不为nn),表示新建的变量名,xx 和 yy 可能是正整数或 nn ,已知若为正整数则一定小于 100。
程序行若以E
开头,则表示循环体结束。
输出格式
输出文件共 tt 行,对应输入的 tt 个程序,每行输出Yes
或No
或者ERR
(输出中不包含引号),若程序实际复杂度与输入给出的复杂度一致则输出Yes
,不一致则输出No
,若程序有语法错误(其中语法错误只有: ① F 和 E 不匹配 ②新建的变量与已经存在但未被销毁的变量重复两种情况),则输出ERR
。
注意:即使在程序不会执行的循环体中出现了语法错误也会编译错误,要输出 ERR
。
输入输出样例
输入 #1复制
8 2 O(1) F i 1 1 E 2 O(n^1) F x 1 n E 1 O(1) F x 1 n 4 O(n^2) F x 5 n F y 10 n E E 4 O(n^2) F x 9 n E F y 2 n E 4 O(n^1) F x 9 n F y n 4 E E 4 O(1) F y n 4 F x 9 n E E 4 O(n^2) F x 1 n F x 1 10 E E
输出 #1复制
Yes Yes ERR Yes No Yes Yes ERR
说明/提示
【输入输出样例解释1】
第一个程序 ii 从 1 到 1 是常数复杂度。
第二个程序 xx 从 1 到 nn 是 nn 的一次方的复杂度。
第三个程序有一个 F
开启循环却没有 E
结束,语法错误。
第四个程序二重循环,nn 的平方的复杂度。
第五个程序两个一重循环,nn 的一次方的复杂度。
第六个程序第一重循环正常,但第二重循环开始即终止(因为nn远大于100,100大于4)。
第七个程序第一重循环无法进入,故为常数复杂度。
第八个程序第二重循环中的变量 xx 与第一重循环中的变量重复,出现语法错误②,输出 ERR
。
【数据规模与约定】
对于 30\%30%的数据:不存在语法错误,数据保证小明给出的每个程序的前 L/2L/2 行一定为以 F
开头的语句,第 L/2+1L/2+1 行至第 LL 行一定为以 EE 开头的语句,L \le 10L≤10,若 xx、yy 均 为整数,xx 一定小于 yy,且只有 yy 有可能为 nn。
对于 50\%50%的数据:不存在语法错误,L \le 100L≤100,且若 xx、yy 均为整数,xx 一定小于 yy, 且只有 yy 有可能为 nn。
对于 70\%70%的数据:不存在语法错误,L \le 100L≤100。
对于 100\%100%的数据:L \le 100L≤100。
如果需要Hack请私信@zhouyonglong或发讨论,提供数据和能Hack掉的本题的AC记录。
#include<bits/stdc++.h>
using namespace std;
int T,dep,l,qw1,qw2,rl,numc,numd,mx,nu,jsq,jsq2;//dep为深度,qw1为常数,qw2为n次方 ,rl1为常数,rl2为次方
bool fzdn,pd1,pd2,pd3;//是否为n
char a[5000],A,B,C[4000],D[4000];//abcd为1234个字符
stack<int>M;
vector<char>N;//判断是否有重复的变量名
int f;//有几个F
int main(){
cin>>T;
while(T--){
dep=0;
pd3=false;
pd1=false;
qw1=qw2=rl=0;
numc=numd=0;
fzdn=false;
f=0;
while(jsq>0)
N.pop_back(),jsq=N.size();
jsq=0;
mx=0;
cin>>l;
cin>>a;
if(a[2]=='n'){
fzdn=true;
for(int I=4;a[I]>='0'&&a[I]<='9';I++){
qw2=(a[I]-'0')+10*qw2;
}
}
else{
for(int i=2;a[i]>='0'&&a[i]<='9';i++){
qw1=(a[i]-'0')+10*qw1;
}
}
for(int i=1;i<=l;i++){
cin>>A;
if(A=='E'){
f--;
if(f<0){
pd3=true;
}
if(dep>0){
dep--;
if(dep==0)
pd1=false;
}
if(M.size()){
nu=M.top();
rl-=nu;
M.pop();
}
if(N.size()){
N.pop_back();
jsq=N.size();
}
}
if(A=='F'){
numc=0;
numd=0;
f++;
cin>>B;
for(int j=0;j<N.size();j++){
if(B==N[j]){
pd3=true;
}
}
if(pd1){
dep++;
cin>>C>>D;
continue;
}
if(!pd3)
N.push_back(B),jsq=N.size();
cin>>C>>D;
if(C[0]!='n'){
if(D[0]=='n'){
rl++;
mx=max(rl,mx);
M.push(1);
}
else{
for(int j=0;C[j]>='0'&&C[j]<='9';j++)
numc=(C[j]-'0')+10*numc;
for(int j=0;D[j]>='0'&&D[j]<='9';j++)
numd=(D[j]-'0')+10*numd;
if(numc>numd)
pd1=true,dep=1;
M.push(0);
}
}
if(C[0]=='n'&&D[0]!='n')
pd1=true,dep=1;
if(C[0]=='n'&&D[0]=='n')
M.push(0);
}
}
if(l%2==1){
cout<<"ERR"<<endl;
continue;
}
if(f!=0&&!pd3){
cout<<"ERR"<<endl;
continue;
}
if(!fzdn&&!pd3){
if(mx==0)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
if(fzdn&&!pd3){
if(mx==qw2)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
if(pd3){
cout<<"ERR"<<endl;
}
}
return 0;
}