C语言程序设计经典例题:输油管道问题、集合划分问题、最少硬币问题、区间覆盖问题、子集和问题

网上有很多同类型的相关例题解法,即便会重复,我还是决定发一下,因为这些都是自己大一写的,也不存在抄袭问题,但当时学识尚浅,如有错误还请指正谅解。
输油管道问题
★问题描述:某石油公同计划建造一条由东向西的主输油管道。该管道要穿过一个有n口油井的油田。从每口油井都要有一条输油管道沿最短路径(或南或北)与主管道相连。如果给定n口油井的位置,即它们的x坐标(东西向)和y坐标(南北向) ,应如何确定主管道的最优位置,即使各油井到主管道之间的输油管道长度总和最小的位置?证明可在线性时间内确定主管道的最优位置。
★算法设计:给定n口油井的位置,计算各油井到主管道之间的输油管道最小长度总和。
★数据输入:由文件input.txt提供输人数据。文件的第1行是油井数n,1≤ n ≤10000。接下来n行是油井的位置,每行2个整数x和y,-10000 ≤ x,y ≤ 10000。
★结果输出:将计算结果输出到文件output.txt。文件的第1行中的数是各油井到主管道之间的输油管道最小长度总和。
输入文件示例(input) 输出文件示例 (output)

input.txtoutput.txt
56
1 2
2 2
1 3
3 -2
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#define N 10001
void swap(int *, int *);  
void quicksort(int *, int, int); //函数声明
void swap(int *p, int *q)    //交换函数
{
    int temp;
    temp = *p;
    *p = *q;
    *q = temp;
    return;
}
void quicksort(int *a, int left, int right)//快速排序法函数
{
    int i = left;
    int j = right;
    int T = a[left];  //定义一个基准
    if (left >= right) 
    {
        return ;
    }
    while (left < right)  
    {
        while (left < right && T <= a[right]) //如果右侧数值比基准大,则将右侧箭头前移
        {
            --right;  
        }
        if (T > a[right])
        {
            swap(&a[left], &a[right]); //当找到右侧比括基准小的数簓时骸,将左侧数与右侧数交换数值
            ++right;
        }
        while (left < right && T >= a[left]) //左侧数小于基准时则不进行操作,并将箭头向右移动
        {
            ++left;  
        }
        if (T < a[left])
        {
            swap(&a[left], &a[right]);  //当找到左侧小于基准的数时则将其与右侧箭头所指向数值进行交换
            --right;
        }
    }
    quicksort(a, i, left-1);  //递归的分别对左侧及右侧的数进行上述操作
    quicksort(a, left+1, j);
}
int main()
{
	FILE *fp,*op;
	int i,j,n=0,mid,s=0;
	int a[N],b[N];
	if((fp=fopen("input.txt","r"))==NULL)  //打开数据输入文件
	{
		printf("cannot open this file\n");
		exit (0);
	}
	rewind(fp);
	fscanf(fp,"%d",&n);
	for(i=0;i<n;i++)
	{
		fscanf(fp,"%d",&a[i]);
		fscanf(fp,"%d",&b[i]);
	}//将文件中的数据读入数组中
	fclose(fp);
	quicksort(b,0,n-1);
	if(n%2==0)
        mid=b[n/2];
	else if(n%2!=0)
		mid=b[(n/2)+1];
	for(j=0;j<n;j++)
	{
	  s=s+abs(b[j]-mid);
	}
	op=fopen("output.txt","w");
	fprintf(op,"%d",s);//将所得结果写入指定文件
	fclose(op);
	system("pause");
	return 0;
}

集合划分问题
★问题描述:n个元素的集合{1,2,…,n}可以划分为若干非空子集。例如,当n=4时,集合{1,2,3,4}可以划分为15个不同的非空子集如下:

{{1},{2},{3},{4}}{{1,3},{2,4}}
{{1,2},{3},{4}}.{{1,4},{2,3}}
{{1,3},{2},{4}}{{1,2,3},{4}}
{{1,4},{2},{3}}{{1,2,4},{3}}
{{2,3},{1},{4}}{{1,3,4},{2}}
{{2,4},{1},{3}}{{2,3,4},{1}}
{{3,4},{1},{2}}{{1,2,3,4}}
{{1,2},{3,4}}

★算法设计:给定正整数n,计算出n个元素的集合{1,2,…,n}可以划分为多少个不同的非空子集。
★数据输入:由文件input. txt提供输人数据。文件的第1行是元素个数n。
★结果输出:将计算出的不同的非空子集数输出到文件output. txt。

输入文件示例输出文件示例
input. txtoutput, txt
552
(二)源代码
#include<stdio.h>
#include<stdlib.h>
int f(int ,int );
int f(int n,int m)//对n个数划分为m个
{
	if(m==1||n==m)
		return 1;
	else
		return f(n-1,m-1)+f(n-1,m)*m;}//第m个为单独元素和第m个为非单独元素
int main()
{
	int n;
	int sum = 0;
	FILE *fp,*op;
	if((fp=fopen("input.txt","r"))==NULL)
	{
		printf("cannot open this file");
		exit(0);
	}
	fscanf(fp,"%d",&n);
	fclose(fp);
	for(int i=1;i<=n;i++)
	{
		sum+=f(n,i);
	}
	op=fopen("output.txt","w");
	fprintf(op,"%d",sum);
	fclose(op);
	system("pause");
	return 0;
}

最少硬币问题
★问题描述:设有n种不同面值的硬币,各硬币的面值存于数组T[1:n]中。现要用这些面值的硬币来找钱。可以使用的各种面值的硬币个数存于数组Coins[1:n]中。对任意钱数0≤m≤ 20 001,设计一个用最少硬币找钱m的方法。
★算法设计:对于给定的1≤ n≤ 10,硬币面值数组T和可以使用的各种面值的硬币个数数组Coins,以及钱数m, 0≤m≤ 20 001,计算找钱m的最少硬币数。
★数据输入:由文件input. txt提供输人数据,文件的第1行中只有1个整数给出n的值,第2行起每行2个数,分别是T[i]和Coins[j].最后1行是要找的钱数m。
★结果输出:将计算出的最少硬币数输出到文件output. tx。问题无解时输出-1。

输入文件示例输出文件示例
input. txtoutput. txt
35
3 5
1 3
2 3
5 3
18
#include<stdio.h>
#include<stdlib.h>
int min(int ,int );
int main()
{
	FILE *fp,*op;
	int i,j,k,m,n;
	int tm[20002]={0};//用tm数组表示m为i时最少硬币数
	if((fp=fopen("input.txt","r"))==NULL)
	{
		printf("cannot open this file\n");
		exit(0);
	}
	rewind(fp);
	fscanf(fp,"%d",&n);
	int *T=new int[n+1];//由于n从文件中读入,在c语言中定义数组时不能用变量,进而采用c++中的动态整型数组,使得n的知晓与否不再有影响,T数组中存放的是不同的面值
	int *coins=new int[n+1];//不同面值的硬币的个数
	for(i=1;i<n+1;i++)
	{
		fscanf(fp,"%d",&T[i]);
		fscanf(fp,"%d",&coins[i]);
	}
	fscanf(fp,"%d",&m);
	fclose(fp); 
    for(i=1;i<=m;i++)
       tm[i]=99999;//初始化tm中的各个值
    for(i=1;i<=n;i++)    
       for(j=1;j<=coins[i];j++)   
           for(k=m;k>=T[i];k--)    
           {
               tm[k]=min(tm[k-T[i]]+1,tm[k]);//此处是算法的核心,是典型的的动态规划型算法,我们希望如同算阶乘那样,将不同钱数m的最少硬币数问题简化为求m-1最少硬币问题,并进行到出现最简单的子问题(m=1)为止
           }
	op=fopen("output.txt","w");
	if(tm[m]!=99999)
		fprintf(op,"%d",tm[m]);
	else
		fprintf(op,"%d",-1);
	fclose(op);
	system("pause");
}
int min(int a,int b)//求较小值函数
{
   return a<b?a:b;
}

区间覆盖问题
★问题描述:设x1,x2,…,xn是实直线上的n个点。用固定长度的闭区间覆盖这n个点,至少需要多少个这样的固定长度闭区间?设计解此问题的有效算法,并证明算法的正确性。
★算法设计:对于给定的实直线上的n个点和闭区间的长度k,计算覆盖点集的最少区间数。
★数据输入:由文件input.xt给出输入数据。第1行有2个正整数n和k,表示有n个点,且固定长度闭区间的长度为k。接下来的1行中,有n个整数,表示n个点在实直线上的坐标(可能相同)。
★结果输出:将计算的最少区间数输出到文件output. txt。

输入文件示例输出文件示例
input. txtoutput.txt
7 33
1 2 3 4 5 -2 6
#include <stdio.h>
#include<stdlib.h>
int X[1000];
void swap(int *, int, int );
void sort(int*,int*);
int greedy(int,int);
void sort(int x[],int left,int right)//快速排序法
{
    int i,t;
    void swap(int x[],int i,int j);
    if(left>=right)
        return; 
    swap(x,right,(left + right)/2);
    t = left;
    for(i=left;i<=right-1;i++)
        if(x[i]<=x[right])
            {
                swap(x,t,i);
                t+=1;
            }
    swap(x,right,t);
    sort(x,left,t - 1);
    sort(x,t + 1,right);
}
void swap(int v[], int i, int j)//交换函数
{
    int temp;
    temp = v[j];
    v[j] = v[i];
    v[i] = temp;
}
int greedy(int n, int k)//算法核心部分,使用贪心算法原理
{
    int i = 1;
    int dist = 0;//dist用于记录i之前未被覆盖的距离值
    int d;
    int count = 0;//记录需要的区间个数
    int t;  //记录覆盖区间最右边
    while(i<n)
    { 
        d = X[i] - X[i-1];
        if(dist + d > k) //如果i之前的距离加上到下一个点之间的距离大于固定区间长度
        {
            dist = 0;
            count++;
            t = i-1;
        }
        dist += d;
        ++i;
    }
    if(t < n) //如果剩余的点间的距离和不够k,也应该用一个新的区间覆盖
        count++;
    return count;
}

int main()
{	
	FILE *fp,*op;
	int i,n,k;

    if((fp=fopen("input.txt","r"))==NULL)
	{
		printf("cannot open this file\n");
			exit(0);
	}
	rewind(fp);
	fscanf(fp,"%d",&n);
	fscanf(fp,"%d",&k);
	for(i=0;i<n;i++)
	{
		fscanf(fp,"%d",&X[i]);
	}
	fclose(fp);
	sort(X,0,n-1);  //将各点坐标排序
	op=fopen("output.txt","w");
	fprintf(op,"%d",greedy(n,k));
	fclose(op);
	system("pause");
    return 0;
} 

子集和问题
部分公式难以显示,看图片吧!最后是 输出“No Solution!”
子集合问题描述

输人文件示例输出文件示例
input. txtoutput. txt
5 102 2 6
2 2 6 5 4
#include<stdio.h>
#include<stdlib.h>
int recall();
int n; // 输入长度
int c; // 和
int *s; // 数据
char *x; // 所求子集元素,与输入数据对应,'Y'为取
// 回朔求解:有解返回非0值,无解返回0
int recall()
{
    int p = 0; // 指向当前值
    int t = 0; // 当前子集合之和
    while(p >= 0)
	{
        if(x[p] == 'N')
		{
            // 选中当前项
            x[p] = 'Y';
            t =t+s[p];
            if(t == c)
			{
                return 1;
            }
            else if(t > c)
			{
                x[p] ='N';
                t =t-s[p];
            }
            p++;
        }
        if(p >= n)
{            // 开始回朔
            while(x[p-1] == 'Y')
			{  p--;
                x[p] = 'N';
                t =t-s[p];
                if(p < 1)
			   return 0;
            }
            while(x[p-1]=='N')
			{
                p--;
                if(p < 1)
				{
                    return 0;
                }
            }
            x[p-1] = 'N';
            t=t-s[p-1];
        }
    }
    return 0;
}
void main()
{
int i;
    FILE *fp,*op;
    if((fp=fopen("input.txt","r"))==NULL)
	{
		printf("cannot open this file\n");
		exit(0);
	}
	rewind(fp);
    fscanf(fp,"%d",&n);
	fscanf(fp,"%d",&c);
    s = (int*)malloc(sizeof(int)*n);
    x = (char*)malloc(sizeof(char)*n);
    for(i=0; i<n; i++)
    {
        fscanf(fp,"%d",&s[i]);
		x[i] = 'N';
    }
	fclose(fp);
	op=fopen("output.txt","w");
    if(recall())//判断是否存在子集
	{
		for(i = 0; i < n; i++)
			
		{	if(x[i]=='Y')
            fprintf(op,"%d\t",s[i]);
		   else continue;
        }
    }
    else
	{
        fputs("No Solution!",op);
	}
    fclose(op);
    system("pause");
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

輕塵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值