贪心算法主要思路
①建立数学模型模拟问题
②拆分问题寻找局部最优解
③集合局部解
旅行家的预算
Description
一个旅行家想驾驶汽车以最少的费用从一个城市 到另一个城市(假设出发时油箱是空的)。给定两个城市之间的距离D1、汽车油箱的容量C(以升为单位)、每升汽油能行驶的距离D2、出发点每升汽油价格P 和沿途油站数N(N可以为零),油站i离出发点的距离Di、每升汽油价格Pi(i=1,2,……N)。计算结果四舍五入至小数点后两位。如果无法到达目的 地,则输出“No Solution”。
Input
第一行为4个实数D1、C、D2、P与一个非负整数N;
接下来N行,每行两个实数Di、Pi。
Output
如果可以到达目的地,输出一个实数(四舍五入至小数点后两位),表示最小费用;否则输出“No Solution”(不含引号)。
Sample Input
275.6 11.9 27.4 2.8 2
102.0 2.9
220.0 2.2
Sample Output
26.95
#include<bits/stdc++.h>
using namespace std;
double d[1000001],p[1000001],d1,v,c,p1,cost,oilremain,dis;
int n,sta,i;
int main()
{
cin>>d1>>c>>v>>p1>>n;
for(i=1;i<=n;i++)
cin>>d[i]>>p[i];
dis=c*v;//油箱加满最远行驶距离
p[0]=p1;
d[n+1]=d1;
p[n+1]=0;//有助于判断在第一个if中直接到终点并且第二个if中直接加满不用考虑会超出终点
do{
bool flag=0;
for(i=sta+1;dis>=(d[i]-d[sta])&&i<=n+1;i++)//找到最近并且可到达的加油站 (在最远可到达的范围内)
{
if(p[sta]>p[i])//更便宜
{
if(oilremain*v>=d[i]-d[sta])
oilremain-=(d[i]-d[sta])/v;
else
{
cost+=p[sta]*((d[i]-d[sta])/v-oilremain);
oilremain=0;
}
flag=1;//标记找到
sta=i;//防止sta受影响
break;
}
}
if(!flag)//找到的话继续do循环找是否还有更便宜的,没找到则继续
{
cost+=p[sta]*(c-oilremain);
oilremain=c-(d[sta+1]-d[sta])/v;//与i无关,到下一站继续寻找更便宜的
if(oilremain>=0) sta++;
else
{
cout<<"No Solution"<<endl;
return 0;
}
}
}while(sta<n+1);
printf("%.2lf\n",cost);
return 0;
}
完美的代价
Description
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。
小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如: mamad
第一次交换 ad : mamda;
第二次交换 md : madma;
第三次交换 ma : madam (回文!完美!)
Input
第一行是一个整数N,表示接下来的字符串的长度(N ≤ 8000)。
第二行是一个字符串,长度为N.只包含小写字母。
Output
如果可能,输出最少的交换次数。
否则输出Impossible。
Sample Input
5
mamad
Sample Output
3
#include<bits/stdc++.h>
using namespace std;
int i,j,n,flag,ans;
string a;
int main()
{
cin>>n>>a;
int l=n-1;//完美跳过无法配对的字符
for(i=0;i<l;i++)
{
for(j=l;j>=i;j--)
{
if(i==j)
{
if(n%2==0||flag)//为单数有一个无法配对字符,或者n为双数有两个
{
cout<<"Impossible"<<endl;
return 0;
}
flag++;
ans+=n/2-i;//如有无法配对字符最后再交换(可直接不交换)
}
else if(a[i]==a[j])
{
for(int k=j;k<l;k++)
{
swap(a[k],a[k+1]);
ans++;
}
l--;//又配好一对,匹配范围缩小
break;
}
}
}
cout<<ans<<endl;
return 0;
}
翻硬币
小明正在玩一个“翻硬币”的游戏。
桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。
比如,可能情形是:oo*oooo
如果同时翻转左边的两个硬币,则变为:oooo***oooo
现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?
我们约定:把翻动相邻的两个硬币叫做一步操作,那么要求:
Input
两行等长的字符串,分别表示初始状态和要达到的目标状态。每行的长度<1000。
Output
一个整数,表示最小操作步数。
Sample Input
oo
Sample Output
5
主要思想
两种状态中只能有双数不一样状态的硬币,然后两两配对挨个翻
#include<bits/stdc++.h>
using namespace std;
int main()
{
char a[1001],b[1001];
int flag=1,i,k,sum=0;
scanf("%s%s",a,b);
for(i=0;a[i];i++)
{
if(flag==1&&a[i]!=b[i])//头
{
k=i;//计数
flag=0;
}
else if(flag==0&&a[i]!=b[i])//尾
{
sum+=i-k;//推断的出
flag=1;
}
}
cout<<sum<<endl;
return 0;
}