T1 生活大爆炸版石头剪刀布
大水题,不解释,打表强过
代码
#include<iostream>
#include<cstdio>
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
#define M 233
using namespace std;
int win[10][10];//1±íʾµÚÒ»¸öÈËÓ®£¬2±íʾµÚ¶þ¸öÈËÓ®£¬0±íʾƽ¾Ö
//0¼ôµ¶£¬1ʯͷ£¬2²¼£¬3òáòæÈË£¬4˹²¨¿Ë
int n,na,nb;
int A[M],B[M];
int main(){
win[0][0]=0,win[0][1]=2,win[0][2]=1,win[0][3]=1,win[0][4]=2;
win[1][0]=1,win[1][1]=0,win[1][2]=2,win[1][3]=1,win[1][4]=2;
win[2][0]=2,win[2][1]=1,win[2][2]=0,win[2][3]=2,win[2][4]=1;
win[3][0]=2,win[3][1]=2,win[3][2]=1,win[3][3]=0,win[3][4]=1;
win[4][0]=1,win[4][1]=1,win[4][2]=2,win[4][3]=2,win[4][4]=0;
cin>>n>>na>>nb;
int cnta=0,cntb=0;
FOR(i,1,na)scanf("%d",&A[i]);
FOR(i,1,nb)scanf("%d",&B[i]);
FOR(i,1,n){
int x=i%na;
int y=i%nb;
if(x==0)x=na;
if(y==0)y=nb;
if(win[A[x]][B[y]]==1)cnta++;
else if(win[A[x]][B[y]]==2)cntb++;
}
cout<<cnta<<" "<<cntb<<endl;
return 0;
}
T2 联合权值
题目描述
无向连通图 G 有 n 个点,n-1 条边。点从 1 到 n 依次编号,编号为 i 的点的权值为 Wi , 每条边的长度均为 1。图上两点(u, v)的距离定义为 u 点到 v 点的最短距离。对于图 G 上的点
对(u, v),若它们的距离为 2,则它们之间会产生Wu×Wv 的联合权值。
请问图 G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?
输入
输入文件名为 link.in。 第一行包含 1 个整数 n。
接下来 n-1 行,每行包含 2 个用空格隔开的正整数 u、v,表示编号为 u 和编号为 v 的点 之间有边相连。
最后 1 行,包含 n 个正整数,每两个正整数之间用一个空格隔开,其中第 i 个整数表示 图 G 上编号为 i 的点的权值为 Wi。
输出
输出共 1 行,包含 2 个整数,之间用一个空格隔开,依次为图 G 上联合权值的最大值 和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对 10007 取余。
数据说明
对于 30%的数据,1 < n ≤ 100; 对于 60%的数据,1 < n ≤ 2000;
对于 100%的数据,1 < n ≤ 200,000,0 < Wi ≤ 10,000。
题解
这道题看似只有两层,但如果是一个菊花图,那就没有办法了。
其实只要枚举一个点作为中心点,记录它的根节点,那么与这个中心点相连的所有点都可以满足了。因此秩序要维护一下前面几个点的前缀和,每次先与父节点相乘,再乘上前缀和即可。
代码
#include<cstdio>
#include<iostream>
#include<vector>
#define M 200050
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
const int P=10007;
vector<int>edge[M];
int w[M];
int n;
long long ans,sum;
struct Water{
void DFS(int x,int f){
long long cnt=0;
int max1=0,max2=0;
FOR(i,0,edge[x].size()-1){
int y=edge[x][i];
if(y==f)continue;
DFS(y,x);
sum+=1LL*w[y]*w[f];
sum%=P;
sum+=1LL*w[y]*cnt;
sum%=P;
cnt+=w[y];
ans=max(ans,1LL*w[y]*w[f]);
if(w[y]>max1)max2=max1,max1=w[y];
else if(w[y]>max2)max2=w[y];
ans=max(ans,1LL*max1*max2);
}
}
void solve(){
FOR(i,1,n-1) {
int a,b;
scanf("%d%d",&a,&b);
edge[a].push_back(b);
edge[b].push_back(a);
}
FOR(i,1,n)scanf("%d",&w[i]);
DFS(1,0);
cout<<ans<<" "<<sum*2%P<<endl;
}
}perfect;
int main(){
cin>>n;
perfect.solve();
return 0;
}
小结
对于每一道题,如果有一些特殊的情况,如此题只有两层,要找到特殊情况的特殊性质,然后再去思考
T3 飞翔的小鸟
题目描述
Flappy Bird 是一款风靡一时的休闲手机游戏。玩家 需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让 小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了 水管或者掉在地上的话,便宣告失败。
为了简化问题,我们对游戏规则进行了简化和改编:
1. 游戏界面是一个长为 n,高为 m 的二维平面,其中有
k 个管道(忽略管道的宽度)。
2. 小鸟始终在游戏界面内移动。小鸟从游戏界面最左边 任意整数高度位置出发,到达游戏界面最右边时,游 戏完成。
3. 小鸟每个单位时间沿横坐标方向右移的距离为 1,竖直移动的距离由玩家控制。如 果点击屏幕,小鸟就会上升一定高度 X,每个单位时间可以点击多次,效果叠加; 如果不点击屏幕,小鸟就会下降一定高度 Y。小鸟位于横坐标方向不同位置时,上 升的高度 X 和下降的高度 Y 可能互不相同。
4. 小鸟高度等于 0 或者小鸟碰到管道时,游戏失败。小鸟高度为 m 时,无法再上升。
现在,请你判断是否可以完成游戏。如果可以,输出最少点击屏幕数;否则,输出小鸟 最多可以通过多少个管道缝隙。
输入
第 1 行有 3 个整数 n,m,k,分别表示游戏界面的长度,高度和水管的数量,每两个 整数之间用一个空格隔开;
接下来的 n 行,每行 2 个用一个空格隔开的整数 X 和 Y,依次表示在横坐标位置 0~n-1 上玩家点击屏幕后,小鸟在下一位置上升的高度 X,以及在这个位置上玩家不点击屏幕时, 小鸟在下一位置下降的高度 Y。
接下来 k 行,每行 3 个整数 P,L,H,每两个整数之间用一个空格隔开。每行表示一 个管道,其中 P 表示管道的横坐标,L 表示此管道缝隙的下边沿高度为 L,H 表示管道缝隙 上边沿的高度(输入数据保证 P 各不相同,但不保证按照大小顺序给出)。
输出
第一行,包含一个整数,如果可以成功完成游戏,则输出 1,否则输出 0。 第二行,包含一个整数,如果第一行为 1,则输出成功完成游戏需要最少点击屏幕数,
否则,输出小鸟最多可以通过多少个管道缝隙。
题解
这道题可以用动态规划来做,几乎是一道裸的动归,但是直接动归会超时,到底复杂度堆在哪里呢?
我们可以这么认为,由于可以点多次,所以一定会有一些重复的,每次只需要维护前一个坐标与下面一个坐标的DP值即可
代码
/*
written by ShimaKZ
*/
#include<iostream>
#include<cstdio>
#include<queue>
#define M 10050
#define FOR(i,a,b) for(register int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(register int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
const int INFINITY=1e9;
struct click {
int up,down;
} A[M];
int pic[M][1050];
int n,m,k;
int tube[M];
int ans=INFINITY;
struct PERFECT {
int dp[M][1050];
void solve() {
FOR(i,1,n)FOR(j,1,m)dp[i][j]=INFINITY;
FOR(i,1,n) {
FOR(j,A[i-1].up+1,m) {
if(dp[i-1][j-A[i-1].up]+1<dp[i][j])
dp[i][j]=dp[i-1][j-A[i-1].up]+1;
if(dp[i][j-A[i-1].up]+1<dp[i][j])
dp[i][j]=dp[i][j-A[i-1].up]+1;
if(j==m) {
FOR(l,m-A[i-1].up+1,m) {
if(dp[i-1][l]+1<dp[i][j])
dp[i][j]=dp[i-1][l]+1;
if(dp[i][l]+1<dp[i][j])
dp[i][j]=dp[i][l]+1;
}
}
}
FOR(j,1,m-A[i-1].down)
if(dp[i][j]>dp[i-1][j+A[i-1].down])dp[i][j]=dp[i-1][j+A[i-1].down];
FOR(j,1,m)if(pic[i][j])dp[i][j]=INFINITY;
}
FOR(j,1,m)if(dp[n][j]!=INFINITY&&dp[n][j]<ans)ans=dp[n][j];
if(ans!=INFINITY) {
puts("1");
cout<<ans<<endl;
} else {
puts("0");
int mx=0;
DOR(i,n,0) {
bool flag=false;
FOR(j,1,m) {
if(dp[i][j]!=INFINITY) {
mx=i;
flag=true;
break;
}
}
if(flag)break;
}
int check=0;
FOR(i,1,k)if(tube[i]<=mx)check++;
cout<<check<<endl;
}
}
} perfect;
int main() {
cin>>n>>m>>k;
FOR(i,0,n-1)scanf("%d%d",&A[i].up,&A[i].down);
FOR(i,1,k) {
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
tube[i]=a;
FOR(j,0,b)pic[a][j]=1;
FOR(j,c,m)pic[a][j]=1;
}
perfect.solve();
return 0;
}
总结
这次考得其实也不是很理想,最后一题的BFS没有写对,反而DP水了70分,结果切分了只有30分,今后的比赛,如果暴力很麻烦并且其他的方法很显然,并且很简单,优先写其他的方法