算法课设(巨水)和一些吐槽

首先我要吐槽一下,大二下学期的课程才有算法,而且学的都是人家初中生玩剩下的普及组的题,这个课程安排真的是有毛病。先学数据结构和算法,把硬件(数字逻辑、计算机组成原理)和经济学原理、管理学这些没什么用的课程砍掉不香吗?讲道理,大学的计科专业真的是什么都学,什么都不精,包括一些 我认为 没什么用的课(我不喜欢硬件和背书),浪费时间。这么一看,一直在学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
*/

猜你喜欢

转载自blog.csdn.net/ljw_study_in_CSDN/article/details/107283637