题一:
学习使用 next_permutation prev_permutation
发现函数next_permutation()是按照字典序产生排列的,并且是从数组中当前的字典序开始依次增大直至到最大字典序
比如初始数据是 1 2 3
使用next_permutation(a,a+n) (n==3)后生成 1 3 2
连续使用后依次生成 2 1 3 | 2 3 1 | 3 1 2 | 3 2 1
当当前序列不存在下一个排列时,函数返回false,否则返回true
当 3 2 1后在使用会直接返回false
同时他是从目前开始依次递推,所以比如数组初始是 2 1 3的话,他下一次会直接是2 3 1,所以假如需要全排列的话建议使用sort先排序一次,否则只能找出该序列之后的全排列数
此外,next_permutation(node,node+n,cmp)可以对结构体num按照自定义的排序方式cmp进行排序。
也可以对字符,因为字符的比较根本还是ascii码
实战演练 : POJ 1256
题目重点:'A'<'a'<'B'<'b'<...<'Z'<'z'.所以使用cmp自定义sort和next_permutation的排序规则就可以做出
//#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <string> #include <cmath> #include <queue> #include <vector> #include <map> #include <set> using namespace std; #define maxn 1000005 #define MAXN 1000000 #define INF 0x7fffffff #define inf 0x3f3f3f3f #define ll long long #define iossync ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define ms(a) memset(a,0,sizeof(a)) #define mss(a) memset(a,-1,sizeof(a)) #define msi(a) memset(a,inf,sizeof(a)) using namespace std; char a[20]; int cmp(char a ,char b) { if(tolower(a)!=tolower(b)) return tolower(a)<tolower(b); return a<b; } int main() { iossync int T; cin>>T; while(T--) { cin>>a; int n=strlen(a); sort(a,a+n,cmp); do{ cout<<a<<" "; }while(next_permutation(a,a+n,cmp)); } return 0; }
题二
洛谷正常刷题
分析:找出字典序最小的,先打表打出杨辉三角的前12行各个子项前的数,然后用dfs爆搜就行,剪下枝干,不然会tle
#include <bits/stdc++.h> using namespace std; int mapp[15][15]={ { 0 }, { 1 } , // N = 1 { 1 , 1 } , // N = 2 { 1 , 2 , 1 } , // N = 3 { 1 , 3 , 3 , 1 } , // N = 4 { 1 , 4 , 6 , 4 , 1 } , // N = 5 { 1 , 5 , 10, 10, 5 , 1 } , // N = 6 { 1 , 6 , 15, 20, 15, 6 , 1 } , // N = 7 { 1 , 7 , 21, 35, 35, 21, 7 , 1 } , // N = 8 { 1 , 8 , 28, 56, 70, 56, 28, 8 , 1 } , // N = 9 { 1 , 9 , 36, 84,126,126, 84, 36, 9 , 1 } , // N = 10 { 1 , 10, 45,120,210,252,210,120, 45, 10 , 1 } , // N = 11 { 1 , 11, 55,165,330,462,462,330,165, 55 ,11 , 1 } }; // N = 12 int a[15],b[15]; int flag; int n,sum; int dfs(int x,int y) { if(x==n) { if(y==sum) flag=1; return 0; } else { for(int i=1;i<=n;i++) { if(b[i]) continue; if(y+mapp[n][x]*i<=sum) { ++b[i]; a[x+1]=i; dfs(x+1,y+mapp[n][x]*i); b[i]--; if(flag) return 0; } else return 0; } } } int main() { //int n,sum; cin>>n>>sum; dfs(0,0); if(flag) for(int i=1;i<=n;i++) cout<<a[i]<<" "; cout<<" "; return 0; }
同样的,也可以使用next_permutation
#include <bits/stdc++.h> using namespace std; int mapp[15][15]={ { 0 }, { 1 } , // N = 1 { 1 , 1 } , // N = 2 { 1 , 2 , 1 } , // N = 3 { 1 , 3 , 3 , 1 } , // N = 4 { 1 , 4 , 6 , 4 , 1 } , // N = 5 { 1 , 5 , 10, 10, 5 , 1 } , // N = 6 { 1 , 6 , 15, 20, 15, 6 , 1 } , // N = 7 { 1 , 7 , 21, 35, 35, 21, 7 , 1 } , // N = 8 { 1 , 8 , 28, 56, 70, 56, 28, 8 , 1 } , // N = 9 { 1 , 9 , 36, 84,126,126, 84, 36, 9 , 1 } , // N = 10 { 1 , 10, 45,120,210,252,210,120, 45, 10 , 1 } , // N = 11 { 1 , 11, 55,165,330,462,462,330,165, 55 ,11 , 1 } }; // N = 12 int a[15],b[15]; int n,sum; int cmp(int a,int b) { return a>b; } int main() { //int n,sum; cin>>n>>sum; for(int i=1;i<=n;i++) a[i]=i; do{ int add=0; for(int i=1;i<=n;i++) { add+=a[i]*mapp[n][i-1]; if(add>sum)//剪枝的关键,从这里开始的add已经大于sum了,那么以他开头的排列全部可以省去 { sort(a+i+1,a+n+1,cmp); break; } } if(add==sum) { for(int i=1;i<=n;i++) cout<<a[i]<<" "; cout<<" "; return 0; } }while(next_permutation(a+1,a+n+1)); return 0; }
题三
紧跟题二的训练场 P1434
分析:傻逼题,一开始看错题意了,以为求最长的轨道的最高点到最低点的距离,2发20分,思考了半天为啥错,才发现题意不对
动态规划
#include <bits/stdc++.h> using namespace std; int n,m; int a[505][505]; int bp[505][505]; int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0}; int dfs(int x,int y) { if(bp[x][y]>=0) return bp[x][y]; //int flag=1; bp[x][y]=1; for(int i=0;i<4;i++) { int x1=x+dx[i],y1=y+dy[i]; if(x1<0 || x1>=n || y1<0 || y1>=m) continue; if(a[x1][y1]<a[x][y]) { //flag=0; //bp[x][y]=max(bp[x][y],dfs(x1,y1)+a[x][y]-a[x1][y1]);看错题意了 bp[x][y]=max(bp[x][y],dfs(x1,y1)+1); } } //if(flag) //bp[x][y]=a[x][y]; return bp[x][y]; } int main() { memset(bp,-1,sizeof(bp)); cin>>n>>m; for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { cin>>a[i][j]; } } int maxx=0; for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { maxx=max(dfs(i,j),maxx); } } cout<<maxx<<" "; return 0; }
试着用dp重新写了一个,并结合了优先队列
#include <bits/stdc++.h> using namespace std; int n,m; int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0}; struct node{ int i1,j1,h1,sum1; }a[105]; int cmp(node x,node y) { return x.h1<y.h1; } int mapp[105][105]; int sum[105][105]; struct cmp1{ bool operator()(node x,node y){ return x.h1>y.h1; } };//优先队列小的在前面 priority_queue<node,vector<node>,cmp1>q; int main() { cin>>n>>m; //int num=0; //第一种方法按顺序读入,然后使用sort排序,然后查找 // for(int i=0;i<n;i++) // { // for(int j=0;j<m;j++) // { // cin>>a[num].h1; // a[num].i1=i; // a[num++].j1=j; // } // } // sort(a,a+num,cmp); //第二种方法,使用优先队列 for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { sum[i][j]=1;//本身长度为1 cin>>mapp[i][j]; node w; w.h1=mapp[i][j],w.i1=i,w.j1=j; w.sum1=0; q.push(w); } } int maxn=0; while(!q.empty()) { node qwe=q.top(); int x=qwe.i1,y=qwe.j1; q.pop();//出队 for(int i=0;i<4;i++) { int x1=x+dx[i],y1=y+dy[i]; if(x1<0 || x1>=n || y1<0 || y1>=m) continue; if(mapp[x1][y1]<mapp[x][y]) { sum[x][y]=max(sum[x][y],sum[x1][y1]+1); } } maxn=max(maxn,sum[x][y]); } cout<<maxn<<" "; return 0; }
题四
洛谷P1433 吃奶酪
分析:emm,先打了一个暴力dfs,然后90分,最后一组会tle
#include <bits/stdc++.h> using namespace std; typedef pair<double,double> P; P a[20]; int n; double fact(P x,P y) { return sqrt((x.first-y.first)*(x.first-y.first)+(x.second-y.second)*(x.second-y.second)); } int vis[20];//标记奶酪是否已经被拾取 double ans=1000000000.0,now,dis[20][20]; void dfs(int pos,int num) { //cout<<pos<<" "<<num<<" "<<now<<" "; if(now>ans) return ; if(num==n) { ans=min(ans,now); return ; } vis[pos]=1; for(int i=1;i<=n;i++) { if(!vis[i]) { now+=dis[pos][i]; dfs(i,num+1); now-=dis[pos][i]; } } vis[pos]=0; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); //cin>>n; scanf("%d",&n); for(int i=1;i<=n;i++) { //cin>>a[i].first>>a[i].second; scanf("%lf %lf",&a[i].first,&a[i].second); } a[0].first=0,a[0].second=0; for(int i=0;i<=n;i++)//预处理2点距离 { for(int j=0;j<=n;j++) { //dis[i][j]=fact(a[i],a[j]); dis[i][j]=sqrt((a[i].first-a[j].first)*(a[i].first-a[j].first)+(a[i].second-a[j].second)*(a[i].second-a[j].second)); } } dfs(0,0); printf("%.02lf ",ans); //cout<<ans<<" "; return 0; }
那没办法了,
#include <bits/stdc++.h> using namespace std; double x[20],y[20],dis[20][20]; double num[20][40000]; int main() { int n; cin>>n; memset(num,127,sizeof(num)); // 15个数字 (1<<15)-1 //dis[x][y] 从x遍历到y的距离最小值 //dis[x][y]=min(dis[x][y],dis[a][y-1<<(i-1)]+num[a][x] for(int i=1; i<=n; i++) { //cin>>x[i]>>y[i]; scanf("%lf %lf",&x[i],&y[i]); } //优先预处理各个位置的距离,方便后续计算 x[0]=0,y[0]=0; for(int i=0; i<=n; i++) { for(int j=0; j<=n; j++) { dis[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])); } } int maxn=(1<<n)-1; for(int s=1;s<=maxn;s++)//00000001 { for(int i=1;i<=n;i++) { if((s&(1<<(i-1)))==0) continue; if(s==(1<<(i-1))) { num[i][s]=0; continue; } for(int j=1;j<=n;j++) { if(s&(1<<(j-1))==0) continue; if(i==j) continue; num[i][s]=min(num[i][s],num[j][s-(1<<(i-1))]+dis[i][j]); } } } double ans=1000000000; for(int i=1;i<=n;i++) { ans=min(ans,num[i][(1<<n)-1]+dis[i][0]); } printf("%.2lf ",ans); return 0; }
使用状压,这题不能用一般的dfs剪枝,数据似乎加强了,题解区的dfs全部过不去(一开始我还以为是我的dfs剪得不对,改了好久好久好久。。。)
题五
洛谷P1020 导弹拦截
分析:LIS裸题,求一遍LIS,再反过来再求一遍就好了,记得第二遍那个不是完全递减,可以存在相同的,所以lower_bound要改成upper_bound
刚好复习下LIS的模板
#include <bits/stdc++.h> using namespace std; int dp[200000]; int a[200000]; #define INF 100000000 int main() { //LIS部分 int n=0; //cin>>n; fill(dp,dp+n,INF); //for(int i=0;i<n;i++) //{ // cin>>a[i]; //} while(cin>>a[n++]) ; n--; int num=0; dp[0]=a[0]; for(int i=1;i<n;i++) { if(a[i]>dp[num]) { dp[++num]=a[i]; } else { *lower_bound(dp,dp+num,a[i])=a[i]; } } //cout<<num+1<<" "; for(int i=0;i<(n+1)/2;i++) { swap(a[i],a[n-1-i]); } int sum=0; fill(dp,dp+n,INF); dp[0]=a[0]; for(int i=1;i<n;i++) { if(a[i]>=dp[sum]) { dp[++sum]=a[i]; } else { // cout<<*upper_bound(dp,dp+n,a[i])<<" "; *upper_bound(dp,dp+n,a[i])=a[i]; } } cout<<sum+1<<" "<<num+1<<" "; return 0; }