首先我要吐槽一下,大二下学期的课程才有算法,而且学的都是人家初中生玩剩下的普及组的题,这个课程安排真的是有毛病。先学数据结构和算法,把硬件(数字逻辑、计算机组成原理)和经济学原理、管理学这些没什么用的课程砍掉不香吗?讲道理,大学的计科专业真的是什么都学,什么都不精,包括一些 我认为 没什么用的课(我不喜欢硬件和背书),浪费时间。这么一看,一直在学ACM确实挺好的,现在看这些题觉得特别傻。
随便吐槽一下,反正这文章也没几个人能看到。
一、回溯法:素数环(UVA524)
#include <bits/stdc++.h>
using namespace std;
const int N=35;
int n,x[N+10];
bool vis[N+10],isprime[N+10];
bool judge(int x) // 判断是否为素数
{
if(x<=1)return 0;
for(int i=2;i*i<=x;i++)
if(x%i==0)return 0;
return 1;
}
void get_prime() // 打表isprime数组
{
for(int i=1;i<=N;i++)
isprime[i]=judge(i);
}
void out(int x[])
{
for(int i=1;i<=n;i++)
i==n?printf("%d\n",x[i]):printf("%d ",x[i]);
}
void dfs(int k)
{
if(k>n)
{
if(isprime[x[n]+x[1]])
out(x);
return;
}
for(int i=2;i<=n;i++)
{
x[k]=i;
if(!vis[i]&&isprime[x[k-1]+x[k]])
{
vis[i]=1;
dfs(k+1);
vis[i]=0;
}
}
}
int main()
{
ios::sync_with_stdio(false);
get_prime();
int cas=0;
while(cin>>n)
{
if(cas)printf("\n"); // 注意输出格式,从第二行开始首先打印一个空行
printf("Case %d:\n",++cas);
if(n%2==0) // 偶数才有解(当然,题目保证了一定有解)
{
for(int i=1;i<=n;i++)
x[i]=i;
memset(vis,0,sizeof(vis));
dfs(2);
}
}
return 0;
}
二、分治法:整数因子分解问题(SDUT 1722)
方法一,比较朴素的递归分治:
#include <bits/stdc++.h>
using namespace std;
int n,sum=1;
void dfs(int x)
{
for(int i=2;i*i<=x;i++)
{
if(x%i==0)
{
//printf("%d=%d*%d sum+=%d\n",x,i,x/i,i*i==x?1:2);
dfs(i);
if(i*i!=x)
{
sum+=2;
dfs(x/i);
}
else sum++;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
dfs(n);
printf("%d\n",sum);
return 0;
}
/*
2000000000
ans:1223682048
1000000000
ans:374416128
12=12 sum=1
12=2*6=6*2 sum+=2
12=2*(2*3)=(2*3)*2 sum+=2
12=3*4=4*3 sum+=2
12=3*(2*2)=(2*2)*3[重复] sum+=1
最后,sum=8
36=36 sum=1
36=2*18 sum+=2
18=2*9 sum+=2
9=3*3 sum+=1
18=3*6 sum+=2
6=2*3 sum+=2
36=3*12 sum+=2
12=2*6 sum+=2
6=2*3 sum+=2
12=3*4 sum+=2
4=2*2 sum+=1
36=4*9 sum+=2
4=2*2 sum+=1
9=3*3 sum+=1
36=6*6 sum+=1
6=2*3 sum+=2
26
*/
实际上2e9肯定超时了,数据比较弱,能过。
方法二,记忆化搜索,0ms AC:
#include <bits/stdc++.h>
using namespace std;
int n;
map<int,int>dp; // dp记录搜索过的值,下次遇见就不再搜索
int dfs(int x) // dp[x]与dfs(x)返回值相等
{
if(dp[x])return dp[x]; // 以前搜索过x并保存在dp[x]了,再遇见就不再搜索
int sum=1; // sum改成局部变量
for(int i=2;i*i<=x;i++)
{
if(x%i==0)
{
sum+=dfs(i);
if(i*i!=x)
sum+=dfs(x/i);
}
}
dp[x]=sum;
return dp[x];
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
int ans=dfs(n);
printf("%d\n",ans);
return 0;
}
/*
2000000000
ans:1223682048
*/
剩下两个水题,我都懒得去oj找原题测试了,随便看看就好…
三、动态规划
给定一个矩阵m,从左上角开始每次只能向右走或者向下走,最后达到右下角的位置,路径中所有数字累加起来就是路径和,返回所有路径的最小路径和,如果给定的m如下,那么路径1,3,1,0,6,1,0就是最小路径和,返回12.
1 3 5 9
8 1 3 4
5 0 6 1
8 8 4 0
#include <bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m,dp[N][N],a[N][N];
struct node
{
int x,y;
}path[N][N],ans[N*N];
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
if(i==1) // 第一行只能由左边走到
{
dp[i][j]=dp[i][j-1]+a[i][j];
path[i][j]={
i,j-1};
}
else
{
if(dp[i-1][j]<=dp[i][j-1])
{
dp[i][j]=dp[i-1][j]+a[i][j];
path[i][j]={
i-1,j};
}
else
{
dp[i][j]=dp[i][j-1]+a[i][j];
path[i][j]={
i,j-1};
}
}
}
}
printf("最短距离:%d\n",dp[n][m]);
int x=n,y=m,cnt=0;
while(x>0&&y>0)
{
ans[++cnt]={
x,y};
int tx=x,ty=y;
//printf("%d %d\n",x,y);
x=path[tx][ty].x;
y=path[tx][ty].y;
}
printf("\n最短路径:\n");
for(int i=cnt;i>=1;i--)
printf("(%d,%d)\n",ans[i].x,ans[i].y);
return 0;
}
/*
4 4
1 3 5 9
8 1 3 4
5 0 6 1
8 8 4 0
ans:
最短距离:12
最短路径:
(1,1)
(1,2)
(2,2)
(3,2)
(3,3)
(3,4)
(4,4)
*/
四、贪心
文件连接问题:给定一个大小为n的数组F,数组元素F[i]表示第i个文件的长度。现在需要将所有文件合并成一个文件,文件越长后面连接成新文件花费的时间越长,试给出贪心算法给出文件连接顺序,保证连接文件花费的时间最短。
时间是应该按每次合并两个文件的长度之和来算,就是类似于合并果子问题,代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int t[N];
priority_queue<int,vector<int>,greater<int> >q;
int main()
{
cout<<"请输入文件的个数"<<endl;
int n; cin>>n;
for(int i=0;i<n;i++)
{
cout<<"请输入第"<<(i+1)<<"个文件的长度"<<endl;
cin>>t[i];
q.push(t[i]);
}
int sum=0;
while(q.size()>1)
{
int x=q.top(); q.pop();
int y=q.top(); q.pop();
sum+=x+y;
q.push(x+y);
}
cout<<"合并这些文件的最短时间为"<<sum<<endl;
return 0;
}
/*
4
1
6
3
4
ans:26
*/