Problem 1 | 数正方形(count.cpp/c/pas) | ||
题目描述 | 在n * n的点阵中任取4个点,回答: 问题1:这4个点恰好是“正放”的正方形的4个顶点的方案数是多少? 问题2:这4个点恰好是正方形(包括“正放”和“斜放”)的4个顶点的方案数是多少?
| ||
输入格式 | 两个整数n和k,n表示点阵的尺寸,k=1表示需要回答问题1,k=2表示需要回答问题2 | ||
输出格式 | 一个整数,表示答案。(模1000000007再输出) | ||
输入样例 | 输入样例1: 4 1 | 输入样例2: 4 2 | |
输出样例 | 输出样例1: 14 | 输出样例2: 20 | |
数据范围 | 对于10%的数据,n = 5 其中k=1和k=2各占一半 对于30%的数据,1 <= n <= 50 其中k=1和k=2各占一半 对于100%的数据,1 <= n <= 100000 其中k=1和k=2各占一半 | ||
样例说明 | 无 |
这题其实没什么好说的,就是一个找规律而已,唯一要注意的地方就是取模的顺序,这个地方只要多试几组大样例就能发现问题
期望得分:100,实际得分:100
<span style="color:#000000;">#include<cstdio>
#include<iostream>
#define LL long long
using namespace std;
const LL mod=1000000007;
LL n,k;
int main(){
cin>>n>>k;
LL i,j,sum;
sum=(((n-1)*n)*(2*n-1)/6)%mod;
if(k==1){
cout<<sum;
return 0;
}
for(i=n-2;i;i--)
sum=(sum+i*i%mod*(n-i-1)%mod)%mod;
cout<<sum;
}
/*
98765 2
*/</span>
Problem 2 | 取数(choose.cpp/c/pas) | |||||||||||||||||||||||||||||||||||||||||||||||
题目描述 | n个整数组成的一个环,现在要从中取出m个数,取走一个数字就不能取跟它相邻的数字(相邻的数不能同时取)。要求取出的数字的总和尽可能大,问这个最大和是多少? 如果无解,请输出“Error!” | |||||||||||||||||||||||||||||||||||||||||||||||
输入格式 | 第一行包含两个正整数n、m。 第二行为n个整数Ai。 | |||||||||||||||||||||||||||||||||||||||||||||||
输出格式 | 仅一个整数,表示所求结果。如果无解输出“Error!”,不包含引号。 | |||||||||||||||||||||||||||||||||||||||||||||||
输入样例 | 输入样例1 | 输入样例2 | 样例输入3 | |||||||||||||||||||||||||||||||||||||||||||||
7 3 1 2 3 4 5 6 7 | 7 4 1 2 3 4 5 6 7 | 8 4 8 5 6 2 3 4 8 9 | ||||||||||||||||||||||||||||||||||||||||||||||
输出样例 | 输出样例1 | 输出样例2 | 样例输出3 | |||||||||||||||||||||||||||||||||||||||||||||
15 | Error! | 25 | ||||||||||||||||||||||||||||||||||||||||||||||
数据范围 | 对于全部数据:m<=n;-1000<=Ai<=1000
| |||||||||||||||||||||||||||||||||||||||||||||||
样例说明 | 无 |
这题就有一些难度了,当初考试的时候只想到了暴力dp,原本能过55分差不多,再优化一下能过60分,但是数组开成了1000*1000,直接导致挂了8,9,10,11组原本能过的数据
正解:我们先把每个数字以及它的编号拿入堆中。建大根堆。并记录下每个数左右元素的编号。L[k],R[k]。
最后要输出的答案是ans。初始化为0。
然后每次从堆里面拿出堆顶元素,ans+这个堆定元素的值。
假设我们当前取出的堆顶元素的编号为k。我们现在新生成一个数P[i]=P[L[k]]+P[R[k]]-P[k]。
编号为i的数左边的数的编号我们赋为L[L[k]]。右边的数赋为R[R[k]]。 然后R[R[k]]的左边是i。L[L[k]]右边是i。( 类似于链表的操作)
我们为什么要这么做呢?
因为我们知道,每次选出最大的数肯定不能保证拿到的是最优解。所以我们给自己留个退路,新生成的数,就是取消前面的选择,转过来选择他两旁的数(也就是所谓的改悔)。
期望得分:55,实际得分:20
<span style="color:#000000;">#include<cstdio>
#include<iostream>
#include<queue>
#define PAIR pair<int,int>
#define xx first
#define yy second
using namespace std;
const int maxn=4e5+5;
int n,m,a[maxn],L[maxn],R[maxn],tot,ans;
bool vis[maxn];
priority_queue<PAIR>q;
int main(){
scanf("%d%d",&n,&m);
int i,j;
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
L[i]=i-1;
R[i]=i+1;
q.push(make_pair(a[i],i));
tot++;
}
L[1]=n,R[n]=1;
if(m>n/2){
puts("Error!");
return 0;
}
while(m!=0){
int id=q.top().yy;q.pop();
if(vis[id])continue;
ans+=a[id];
a[++tot]=a[L[id]]+a[R[id]]-a[id];
vis[id]=vis[L[id]]=vis[R[id]]=1;
L[tot]=L[L[id]];
R[tot]=R[R[id]];
L[R[R[id]]]=tot;
R[L[L[id]]]=tot;
q.push(make_pair(a[tot],tot));
m--;
}
cout<<ans;
}</span>
Problem 3 | 葡萄酒交易(wine.cpp/c/pas) | |
题目描述 | 某地分布着N个村庄,编号0到N-1,每个村庄要么需要买酒,要么需要卖酒。 设第i个村庄对葡萄酒的需求为Ai,其中Ai>0表示该村需要买酒,Ai<0表示该村需要卖酒。所有村庄供需平衡,即所有Ai之和等于0 (∑Ai =0)。 不过,只有M对村庄之间存在贸易往来,其中第i对村庄之间无论运输多少葡萄酒,都要花费Ti的运费。请你计算最少需要多少运费就可以满足所有村庄对酒的需求。 | |
输入格式 | 第一行两个整数N、M。 第二行N个整数Ai。 接下来M行每行三个整数pi,qi,Ti,表示在编号为pi和qi的村庄之间运酒需要花费Ti的费用。数据保证每对pi、qi最多出现一次。 | |
输出格式 | 输出一个整数表示答案。无解输出Impossible | |
输入样例 | 3 3 50 -20 -30 0 1 10 1 2 20 0 2 100 | |
输出样例 | 30 | |
数据范围 | 对于 50% 的数据:2<=N<=8。 对于 100% 的数据: | |
样例说明 | 无 |
这题数据比较神奇,直接最小生成树+判断是否有解就能A,不判是否有解就错1组,开始明明想写个最小生成树骗分……,后来发现漏洞百出就放弃了,直接cout"impossible"得了10分,早知道直接搞最小生成树
正解:
1.找出所有酒量和为0的集合。
若该集合的点是联通的,那么求出该集合的最小生成树,生成树的值即是该集合酒量转移所需最小代价
2.将每一个酒量总和为0的集合看成是一个物品,利用背包动规求出最优解。
用二进制来压缩状态,1代表节点在集合中,0代表不在。
比如数字s的二进制形式为100111,表明0,1,2,5号节点在s表示的集合中。
题目最多有n(n<=16)个节点,因此s的范围是0到(2^n)-1 也就是(1<<n)-1
用数组Sum[s],记录集合s中包含的节点的酒量之和。
对于每一个酒量和为0的集合x(Sum[x]==0),若能得到一棵最小生成树,用数组Cost[x]记录下该生成树的代价
f[i]记录平衡集合i中的节点的酒量值,所需最小代价
对于集合i和j,若满足Sum[i]==0且Sum[j]==0
那么有f[i|j]=min(f[i|j],f[i]+Cost[j]);
i|j表示集合i与集合j合并之后的集合
期望得分:90,实际得分:10
<span style="color:#000000;">#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=18,inf=0x3f3f3f3f;
int n,m,a[maxn],f[1<<maxn],sum[1<<maxn],all,p[1<<maxn],fa[maxn];
int getfa(int x){return x==fa[x]?x:fa[x]=getfa(fa[x]);}
struct node{
int a,b,c;
bool operator<(const node& h)const{
return c<h.c;
}
}s[maxn*maxn];
int kruscal(int s0){
int i,fx,fy,x,y,cnt=0,tot=0,ans=0;
for(i=0;i<n;i++){
fa[i]=i;
if((s0>>i)&1)tot++;
}
for(i=1;i<=m;i++){
x=s[i].a,y=s[i].b;
if(((s0>>x)&1)&&((s0>>y)&1)){
fx=getfa(x),fy=getfa(y);
if(fx!=fy){
fa[fx]=fy;
cnt++,ans+=s[i].c;
}
}
}
if(cnt+1!=tot)return inf;
return ans;
}
int main(){
scanf("%d%d",&n,&m);
int i,j;
for(i=0;i<n;i++)scanf("%d",&a[i]);
for(i=1;i<=m;i++)scanf("%d%d%d",&s[i].a,&s[i].b,&s[i].c);
sort(s+1,s+1+m);
all=(1<<n)-1;
for(i=0;i<=all;i++)
for(j=0;j<n;j++)
if((i>>j)&1)sum[i]+=a[j];
for(i=0;i<=all;i++)
if(sum[i]==0)p[i]=kruscal(i);
else p[i]=inf;
memset(f,inf,sizeof(f));
f[0]=0;
for(i=0;i<=all;i++){
if(sum[i]!=0)continue;
for(j=1;j<=all;j++){
if(sum[j]!=0)continue;
f[i|j]=min(f[i|j],f[i]+p[j]);
}
}
if(f[all]<inf)printf("%d",f[all]);
else puts("Impossible");
}</span>
总得分:130,排名:18
总结:代码细节处理还是不够,粗心的毛病还没改掉,下次考试前10分钟一定要认真检查一下自己的代码,减少过失性丢分的概率