A - Little Tiger vs. Deep Monkey
题目链接
题目大意:刷题比赛,老虎做对一道题的概率是0.5,猴子随意(不用管)
求老虎总分不低于猴子的概率p的最低分。
思路:开始我想的是把所有的加和情况放进set去重,然后WA了,题目没读懂,后来看了题解,这是一道概率dp
dp数组含义:dp[i][j]表示前i个科目老虎得分为j的概率
转移方程:
dp[i+1][j+a[i]]+=dp[i][j]*0.5; //做对
dp[i+1][j]+=dp[i][j]*0.5; //没做对
两种情况,做对了这题和没做对这题
#include <bits/stdc++.h>
#define MAX 0x3f3f3f3f
using namespace std;
typedef long long ll;
int t,n;
double p;
double dp[55][50005];
int a[60];
int main(){
cin >> t;
while(t--){
int sum=0;
memset(dp,0,sizeof(dp));//重置
scanf("%d%lf",&n,&p);
for(int i=0; i<n; i++){
scanf("%d",&a[i]);
sum+=a[i];//加和求总分,方便后续dp数组分数那一栏的循环
}
dp[0][0]=1;
for(int i=0; i<n; i++){
for(int k=0; k<=sum; k++){
if(dp[i][k]<=0) continue;
dp[i+1][k+a[i]]+=dp[i][k]*0.5;
dp[i+1][k]+=dp[i][k]*0.5;
}
}
double ans=0;
for(int i=0; i<=sum; i++){
ans+=dp[n][i];//求的是做所有题,所以从n开始
if(ans>=p){
printf("%d\n",i);//i代表的是分数
break;
}
}
}
return 0;
}
挺神奇的,毕竟我对dp不太了解,还可以这样解,学到了。
I - Happy Matt Friends
题目链接
题目大意:给你一个数组,对其中的元素(任意数量)进行异或操作,最后统计有多个结果大于给定的M。
思路:我开始就猜到dp,但我不会,后面查题解,发现这是一道dp位运算
dp数组含义:dp[i][j]表示前i道题中异或值为j的情况的个数。
转移方程:dp[i][j]=dp[i-1][j]+dp[i-1][j^a[i]];
取和不取两种状态
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX=1<<20;//注意一下,不要开0x3f3f3f3f
int t,n,m;
double p;
int dp[55][MAX*2];
int a[60];
int Case;
int main(){
cin >> t;
while(t--){
ll sum=0;
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) scanf("%d",&a[i]);
dp[0][0]=1;
for(int i=1; i<=n; i++){
for(int j=0; j<=MAX; j++){
dp[i][j]=dp[i-1][j]+dp[i-1][j^a[i]];
}
}
for(int i=m; i<=MAX; i++){
sum+=dp[n][i];//从大于m开始加和
}
printf("Case #%d: %lld\n",++Case,sum);
}
return 0;
}
D - Stealing Harry Potter’s Precious
题目链接
题意:一张图,‘#’不可走,‘.’可走,所有的宝物都藏在‘.’里,从‘@’位置出发,如果能找到所有的宝物,输出最短路,如果不能,输出-1。
思路:存图->记下起点->开pair数组记下所有宝物位置->开a数组记下k,用于全排列->全排列的情况下进行bfs,从原点开始,找到一个宝物然后刷新起点至宝物的点,然后不断刷新min步数->输出答案
#include <bits/stdc++.h>
#define MAX 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=105;
int n,m,k;
pii a[maxn];
char mp[maxn][maxn];//存图
int dis[maxn][maxn];//记录到当前点的步数
int dir[4][2]={-1,0,1,0,0,-1,0,1};//方向数组
int st,ed,stx,edx;
int g,h;
int b[maxn];
int bfs(int bx, int by, int ex, int ey){
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++) dis[i][j]=-1;//重置
dis[bx][by]=0;//起点记为0
queue<pii>q;
q.push({bx,by});
while(!q.empty()){
pii t=q.front();
q.pop();
for(int i=0; i<4; i++){
int tx=t.first+dir[i][0], ty=t.second+dir[i][1];
if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&dis[tx][ty]==-1&&mp[tx][ty]=='.'){
dis[tx][ty]=dis[t.first][t.second]+1;
q.push({tx,ty});
}
}
}
return dis[ex][ey];//返回找到宝藏的步数
}
int main(){
while(cin>>n>>m&&n&&m){
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
cin>>mp[i][j];
if(mp[i][j]=='@') st=i,ed=j;
}
}
cin>>k;
for(int i=0; i<k; i++){
cin>>g>>h;
a[i]={g,h};
}
for(int i=0; i<k; i++) b[i]=i;
int sum=MAX;
do{
int res=0;
int nowx=st, nowy=ed;
for(int i=0; i<k; i++){//遍历一遍一种排列情况
int w=bfs(nowx,nowy,a[b[i]].first,a[b[i]].second);
if(w==-1){
//-1是没有找到该宝藏,也就是不存在找到所有宝藏的通路
sum=-1;
goto there;
}
else res+=w;
nowx=a[b[i]].first,nowy=a[b[i]].second;//更新起始点
}
sum=min(sum,res);//刷新最小值
}
while(next_permutation(b,b+k));//全排列,很好用
there:
printf("%d\n",sum);
}
return 0;
}
C - Zhuge Liang’s Password
题目链接
思路:矩阵旋转,水题。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e1+10;
int a[maxn][maxn], b[maxn][maxn], c[maxn][maxn];
int n,sum;
void turn(){
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
c[i][j]=b[n-j-1][i];
}
}
}
void judge(){
int res=0;
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
if(a[i][j]==b[i][j]) res++;
}
}
sum=max(sum,res);
}
int main(){
while(cin>>n&&n){
sum=0;
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
scanf("%d",&a[i][j]);
}
}
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
scanf("%d",&b[i][j]);
c[i][j]=b[i][j];
}
}
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
if(a[i][j]==b[i][j]) sum++;
}
}
for(int i=0; i<3; i++){
turn();
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
b[i][j]=c[i][j];
}
}
judge();
}
printf("%d\n",sum);
}
return 0;
}