#include<bits/stdc++.h> usingnamespace std; int num[10][10]; constdouble PI = acos(-1.0); //用数组标记该行的数哪些可以填,每次填数都进行一个check操作 //check不仅包括行和列,3*3的小格子是难点 //用num[10][10]来记录填了哪些数 //如果遇到填过了,就跳过,没填就依次挑选,依次check。可以填就填,然后dfs下一个点 bool isRowValid[10][10];//记录行的某个数是否可以填 bool isColValid[10][10];//记录列的某个数是否可以填 bool isMatrixValid[4][4][10];//记录第i,j个小方格能否填 boolcheck(int row,int col,int value){ if (isRowValid[row][value]==true && isColValid[col][value]==true) { if(isMatrixValid[(row+2)/3][(col+2)/3][value]==true){//小方格 returntrue; } } returnfalse; } voidnext(int &row,int &col){//下一个点 if (col==9) { row++;col=1; }else{ col++; } } bool ok=false; voiddfs(int row,int col) { if (row==10)//完成 { ok=true; return; } if (num[row][col]>0)//填了 { int newrow=row,newcol=col; next(newrow,newcol); dfs(newrow,newcol); if(ok==true){ return; } }else{ for (int value = 1; value <= 9; value++) { if (check(row,col,value)==true) { num[row][col]=value; isRowValid[row][value]=false; isColValid[col][value]=false; isMatrixValid[(row+2)/3][(col+2)/3][value]=false; int newrow=row,newcol=col; next(newrow,newcol); dfs(newrow,newcol); if(ok==true){ return; } num[row][col]=0; isRowValid[row][value]=true; isColValid[col][value]=true; isMatrixValid[(row+2)/3][(col+2)/3][value]=true; } } if(ok == false){ return; } } } intmain() { int n; cin>>n; char c=getchar();//读换行 while (n--) { //初始化 for(int i=1;i<=9;i++){ for(int value=1;value<=9;value++){ isRowValid[i][value]=true; isColValid[i][value]=true; } } for(int i=1;i<=3;i++){ for(int j=1;j<=3;j++){ for(int value=1;value<=9;value++){ isMatrixValid[i][j][value]=true; } } } ok=false;
//输入 for (int i = 1; i <= 9; i++) { for (int j = 1; j <= 9; j++) { char temp=getchar(); int value=(temp-'0'); num[i][j]=value; isRowValid[i][value]=false; isColValid[j][value]=false; isMatrixValid[(i+2)/3][(j+2)/3][value]=false; } getchar();//读换行 }
dfs(1,1);
//输出 for (int i = 1; i <= 9; i++) { for (int j = 1; j <= 9; j++) { cout<<num[i][j]; } cout<<endl; } } }
4.Set Operation
题目标签
bitset的使用
题目描述
给定n个集合
每个集合中有c个数
接下来给定q个询问
每个询问包含两个数,对于所有的n个集合,判定是否两数在一个集合中
题目解析
对于每一个数,记录他存在的所有集合
对于查询的数a,b只要查询他们所存在过的集合有没有交集即可
如果每个数存在的集合用map+vector来储存,求交集必然会超时
所以这时候bitset就是一个很好的选择
可以用一个数来作为标记,很好的进行了状态压缩
比如7——1101也就是代表在第1,3,4个集合中出现过
而bitset支持单个位数的修改,还能保证占用的空间小
同时求交集只需要将两个数的bitset 进行按位与操作&。
某一位都为1才会保留,也就是都存在过一个集合里就会保留
然后统计他们与的结果里的1个个数是否>0即可
使用bitset自带的count()方法
通过代码
#include<bits.stdc++.h> #define ios ios::sync_with_stdio(false),cin.tie(0) #define ll long long #define ull unsigned long long #define maxnum 500005 #define inf 1000000007 usingnamespace std; //st[i][j]表示i存在的集合 //[j]为桶 intmain() { ll t;scanf("%lld",&t); bitset<1000> mp[10005]; for (ll i = 0; i < t; i++) { ll num; scanf("%lld",&num); for (ll j = 0; j < num; j++) { ll temp; scanf("%lld",&temp); mp[temp][i]=1;//第temp个数的第i位变1 } } ll q;scanf("%lld",&q); for (ll i = 0; i < q; i++) { ll a,b; scanf("%lld %lld",&a,&b); bitset<1000> t=mp[a]&mp[b]; if(t.count()==0){ printf("No\n"); }else{ printf("Yes\n"); } } return0; }
5.BFS - D - Prime Path
题目标签
bfs
题目描述
给你一个素数,和目标数
要求每次改一个位上的数
要求转换完的数也是素数
求最终转换成目标数需要的最少次数
题目解析
bfs广搜
先打素数表
每个数,变换每个位置的数
判断是否位素数
是的话加入,记录当前数经过变换的次数
对进入过队列的数要标记
通过代码
#include<bits/stdc++.h> #define ll long long #define ull unsigned long long #define maxn 500005 #define inf 1000000007 usingnamespace std; constdouble PI = acos(-1.0); bool isprime[maxn]; int prime[maxn]; voidEuler(int n) { int t = 0; for (int i = 2; i <= n; i++) { isprime[i] = true; // 2-n初始化 } for (int i = 0; i <= n; i++) { if (isprime[i]) { prime[t++] = i; } for (int j = 0; j < t && i * prime[j] <= n; j++) { //判断条件j不能超过现有质数的个数,且相乘即被筛的数不能越上界 isprime[prime[j] * i] = false; if (i % prime[j] == 0) { //解析详见下文 break; } } } } intmain() { //打质数表 Euler(10000); int src, dst; int t; cin >> t; while (t--) { map<int, int> mp; map<int, int> vis; cin >> src >> dst; queue<int> q; q.push(src); mp[src] = 0; vis[src]=true; while (!q.empty()) { int curnum = q.front(); if (curnum == dst) { cout << mp[curnum] << endl; break; } for (int i = 1; i <= 9; i += 2) { int newnum = curnum / 10 * 10 + i; if (isprime[newnum] && !vis[newnum]) { q.push(newnum); mp[newnum]=mp[curnum]+1; vis[newnum] = true; } } for (int i = 0; i <= 9; i++) { int newnum = curnum / 100 * 100 + i * 10 + curnum % 10; if (isprime[newnum] && !vis[newnum]) { q.push(newnum); mp[newnum]=mp[curnum]+1; vis[newnum] = true; } } for (int i = 0; i <= 9; i++) { int newnum = curnum / 1000 * 1000 + i * 100 + curnum % 100; if (isprime[newnum] && !vis[newnum]) { q.push(newnum); mp[newnum]=mp[curnum]+1; vis[newnum] = true; } } for (int i = 1; i <= 9; i++) { int newnum = i * 1000 + curnum % 1000; if (isprime[newnum] && !vis[newnum]) { q.push(newnum); mp[newnum]=mp[curnum]+1; vis[newnum] = true; } } q.pop(); } } }
5.BFS - D - Prime Path
题目标签
bfs
题目描述
给你两杯水A.B
你可以进行三种操作
把A,B中的一杯填满
把A,B中的一杯倒掉
把A倒入B/B倒入A
给出三个数分别为
A的容量 B的容量 目标容量
求需要多少步,可以达到最终的容量。
并输出这些步骤
如果不可能则输出”impossible”
题目解析
BFS广搜即可
队列加入的是一个状态
用struct来定义
定义的struct包含
A的水量
B的水量
经过的步骤
注意点:
要用vis数组,对已经产生过的AB的水量进行标记,避免重复进行,无法走出循环
经过的步骤可以用vector来储存,最终输出的时候再转换
通过代码
#include<bits/stdc++.h> #define ll long long #define ull unsigned long long #define maxnum 500005 #define inf 1000000007 usingnamespace std; //储存的是一个自定义的结构体,即当前的状态 //此外还要储存一个字符串数组,记录的时当前状态已经做了哪些操作 int fulA,fulB,dst;
structstate{ int A; int B; int step; vector<int> op; state(int a,int b,int stp){ A=a; B=b; step=stp; } state(void){ A=0; B=0; step=0; } };
while (!q.empty()) { state curState = q.front(); if (curState.A==dst || curState.B==dst) { print(curState.op); return0; }
//三种选择2种方案 //fill A state newState1(fulA,curState.B,curState.step+1); newState1.op=curState.op; newState1.op.push_back(1); if(vis[newState1.A][newState1.B]!=true){ q.push(newState1); vis[newState1.A][newState1.B]=true; } //fill B state newState2(curState.A,fulB,curState.step+1); newState2.op=curState.op; newState2.op.push_back(2); if(vis[newState2.A][newState2.B]!=true){ q.push(newState2); vis[newState2.A][newState2.B]=true; } //Drop A state newState3(0,curState.B,curState.step+1); newState3.op=curState.op; newState3.op.push_back(3); if(vis[newState3.A][newState3.B]!=true){ q.push(newState3); vis[newState3.A][newState3.B]=true; } //Drop B state newState4(curState.A,0,curState.step+1); newState4.op=curState.op; newState4.op.push_back(4); if(vis[newState4.A][newState4.B]!=true){ q.push(newState4); vis[newState4.A][newState4.B]=true; }
//Pour A to B //A>B 剩下的, B加满 int newA,newB; if (curState.A>=fulB-curState.B) { newB=fulB; newA=curState.A-(fulB-curState.B); } //A<B 剩下的, B+A A=0 if (curState.A<fulB-curState.B) { newA=0; newB=curState.B+curState.A; } state newState5(newA,newB,curState.step+1); newState5.op=curState.op; newState5.op.push_back(5); if(vis[newState5.A][newState5.B]!=true){ q.push(newState5); vis[newState5.A][newState5.B]=true; }
//Pour B to A //A>B 剩下的, B加满 if (curState.B>=fulA-curState.A) { newA=fulA; newB=curState.B-(fulA-curState.A); } //A<B 剩下的, B+A A=0 if (curState.B<fulA-curState.A) { newB=0; newA=curState.A+curState.B; } state newState6(newA,newB,curState.step+1); newState6.op=curState.op; newState6.op.push_back(6); if(vis[newState6.A][newState6.B]!=true){ q.push(newState6); vis[newState6.A][newState6.B]=true; } q.pop(); } cout<<"impossible"; }
5.贪心 - D - Prime Path
题目标签
贪心
题目描述
有n档节目,每档节目有开始时间和结束时间
求最多能看完整的节目
题目解析
经典的区间调度问题
因为求的是最多,所以要把节目按照结束的时间来排序
遍历每一个节目,如果下一个节目的开始时间晚于上一个节目的结束时间
那么cnt++
将当前选择的节目更新
通过代码
#include<bits/stdc++.h> #define ll long long #define ull unsigned long long #define maxnum 500005 #define inf 1000000007 usingnamespace std; constdouble PI = acos(-1.0); bool flag=false; structact { int start; int end; }; boolcmp(act a,act b){ if (a.end!=b.end) return a.end<b.end; return a.start<b.start; } intmain() { // ios::sync_with_stdio(false); // cin.tie(0); // freopen("3output.out","w",stdout); // freopen("1input.in","r",stdin); int sum=0; int n; while (cin>>n && n!=0) { sum=0; act a[101]; for (int i = 0; i < n; i++) { cin>>a[i].start>>a[i].end; } sort(a,a+n,cmp); int nowend=a[0].end; sum+=1; for (int i = 1; i < n; i++) { if (a[i].start>=nowend)//下一个活动的时间,开始晚于上一个活动的结束时间 { sum++; nowend=a[i].end;//选下一个活动 } } cout<<sum<<endl; } // printf("Time used = %.2lf\n", (double)clock() / CLOCKS_PER_SEC); return0; }
6.贪心 - E - Doing Homework again
题目标签
贪心
题目描述
小明有n个作业,没有做完
每个作业有相应的分值和截止日期
规定如果某个作业没有在截止日期之前写完,就会扣除相应的分值
请你计算小明扣分最少为多少
题目解析
贪心+日程安排
将所有作业按照分值从大到小来做,尽量把这个作业做的时间靠近他的截止日期的时间
以便把前面的时间留出来做其他作业
分值相同的按照截止日期排名,靠前的排在前面
用一个vis数组来记录当天的日程有没有被安排
先按分值大的来计算,查看他的截止日期那天有没有安排
没有安排,那么就安排在那天。
有安排就往前排,实在没有那就不做
不用担心被扣分,因为如果没有安排的话,说明被安排的都是分值比他大的
所以扣分一定是最小的
通过代码
#include<bits/stdc++.h> #define ll long long #define ull unsigned long long #define maxnum 500005 #define inf 1000000007 usingnamespace std; bool flag=false; structwork { int time; int score; bool vis=false; }; boolcmp(work a,work b){ if(a.score!=b.score){ return a.score>b.score; } return a.time<b.time; } //转化为日程安排问题 //总共n天,从大到小分值进行安排。 //第i个作业,如果原本是第timei天完成,那么就timei完成。如果那天被占,就从后往前安排 //反正是分大的,就算占用了同样本来是这天完成的作业,也没有关系 //vis来记录是否已经完成,最后统计所有没有完成的作业。然后逐个扣分 intmain() { // ios::sync_with_stdio(false); // cin.tie(0); // freopen("3output.out","w",stdout); // freopen("1input.in","r",stdin); int t;cin>>t; while (t--) { int day[1001]={0}; int n;cin>>n; work w[1001]; for (int i = 0; i < n; i++) cin>>w[i].time; for (int i = 0; i < n; i++) cin>>w[i].score; sort(w,w+n,cmp); int sum=0; for (int i = 0; i < n; i++) { if (day[w[i].time]==0)//没有安排 { day[w[i].time]=1; w[i].vis=true; }else{//从后往前 for (int j = w[i].time-1; j >= 1; j--) { if (day[j]==0)//从后往前的没有安排 { day[j]=1; w[i].vis=true; break;//找到了就及时推出,不然一直循环下去把所有日子都占了 } } //没有找到空的,不安排,扣分 } } for (int i = 0; i <n; i++) { if (w[i].vis==false) { sum+=w[i].score; } } cout<<sum<<endl; } return0; }
7.饭卡
题目标签
贪心;背包dp
题目描述
有一张饭卡,当他大于等于五块钱的时候,购买一个东西不管钱够不够,一定能买成功
现在你的饭卡里有x元
共有n个菜
每道菜的价格为ai元
求饭卡最低能花到多少钱
题目解析
要让饭卡最低,肯定要最贵的菜在只有五块钱的时候买
那么接下来就转化成一个01背包的问题
只要让x-5元尽可能被花光即可
通过代码
#include<bits/stdc #define ios ios::sync_with_stdio(false),cin.tie(0) #define ll long long #define ull unsigned long long #define maxnum 500005 #define inf 1000000007 usingnamespace std; constdouble PI = acos(-1.0); //排序出最贵的菜,等到用的只有五块的时候再用 //前n-1个菜就转化为01背包问题 //如果钱的总数已经大于菜的总数,那么直接输出,不用再计算 intmain() { int n; while (cin>>n && n!=0) { int value[2000]={0}; int sum=0; for (int i = 0; i < n; i++) { cin>>value[i]; sum+=value[i]; } int cap; cin>>cap; if(cap-sum>=0){ cout<<cap-sum<<endl; continue; } if(cap<5){ cout<<cap<<endl; continue; } sort(value,value+n); //dp前n-1个菜,每个菜的容量是value[i],价值也是value[i] int dp[2000]={0};//dp[j]:表示,用j块钱能花的最多的菜 for (int i = 0; i < n-1; i++) { for (int j = cap-5; j >=value[i]; j--) { dp[j]=max(dp[j],dp[j-value[i]]+value[i]); } } cout<<cap-dp[cap-5]-value[n-1]<<endl; } return0; }