比赛在HDU进行,网址入口是User Login,密码是acm加训练室号码
1. POJ 1260 Pearls 珍珠 线性DP
问题描述
每个月,皇家珍珠的股票经理都会准备一份清单,列出每个质量等级所需的珍珠数量。珍珠是在当地珍珠市场购买的。每个质量等级每颗珍珠都有自己的价格,但对于某个质量等级的每笔完整交易,必须支付相当于该类别中十颗珍珠的额外金额。这是为了防止游客只买一颗珍珠。
此外,皇家明珠正遭受着全球经济放缓
的影响。因此,公司需要提高效率。首席财务官(首席财务官)发现,他有时可以通过购买比实际需要的更高质量的珍珠来省钱。只要价格保持不变,任何客户都会责怪皇家珍珠在手镯中放入更好的珍珠。
例如,10 欧元类别需要 5 颗珍珠,20 欧元类别需要 100 颗
珍珠。这通常要花费:(5+10)*10 = (100+10)*20 = 2350 欧元。
购买所有 105 颗 20 欧元类别的珍珠仅需花费:(5+100+10)*20 = 2300
欧元。
问题是,在首席财务官知道有多少珍珠最适合在更高质量的类别中购买之前,它需要大量的计算
工作。你被要求帮助皇家珍珠与计算机程序。
鉴于珍珠的数量和不同质量类别的每颗珍珠的价格,给出购买清单中所有东西所需的最低
价格。珍珠可以在要求的,或在更高质量的类购买,但不能在较低的类别购买。
输入
输出
示例输入
2
2
100 1
100 2
3
1 10
1 11
100 12
样品输出
330
1344
问题的解决:
状态表示:
dp[i] : 表示前i个珍珠的最低价格 (本题题干描述满足动态规划无后效性,当前状态与之前的状态有关)
属性:min
状态计算:
状态转移方程为 dp[i]=min(dp[i],dp[j]+(sum[i]-sum[j]+10)*val[i])
括号内第二个式子的含义是:前 j 个数的情况已经确定下来了,j+1到i都由i的价值来计算,即前缀和数组相减得到 j+1到i的珍珠个数
注意点
1.由于min和max不一样,max的Dp数组一般有时候初始全为0,反正max可以挑到比0大的,但min数组不行,因为0比较小,dp[]很可能挑到0,所以要初始化dp的值
2.这题类似于最长上升子序列虽然数组是1维,但要有两个for循环,第二个j是为了让前面的j个数和现在的当前数建立联系,最长上升子序列的公式 dp[i]=max(dp[i],dp[j]+1)
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
using namespace std;
int n,c,a,b;
const int N=200;
int sum[N]; //前缀和数组
int val[N]; //存珍珠的价格
int dp[N];
int main()
{
scanf("%d",&n);
while(n--)
{
scanf("%d",&c);
memset(sum,0,sizeof sum); //while循环,每次都要重置
memset(val,0,sizeof val);
memset(dp,0,sizeof dp);
for(int i=1;i<=c;i++)
{
int a,p;
scanf("%d%d",&a,&p);
sum[i]=sum[i-1]+a;
val[i]=p;
}
for(int i=1;i<=c;i++)
{
dp[i]=(sum[i]+10)*val[i]; //dp[]中存的是全买一样的花费
for(int j=i-1;j>=1;j--)
{
dp[i]=min(dp[i],(sum[i]-sum[j]+10)*val[i]+dp[j]); //因为min和max不太一样min算下来的值很有可能被0取代,所以要在前面先算dp[]的值
}
}
cout<<dp[c]<<endl;
}
}
回文串
Problem Description
Write a program to determine whether a word is a palindrome. A palindrome is a sequence of characters that is identical to the string when the characters are placed in reverse order. For example, the following strings are palindromes: “ABCCBA”, “A”, and “AMA”. The following strings are not palindromes: “HELLO”, “ABAB” and “PPA”.
Input
The input file will consist of up to 100 lines, where each line contains at least 1 and at most 52 characters. Your program should stop processing the input when the input string equals “STOP”. You may assume that input file consists of exclusively uppercase letters; no lowercase letters, punctuation marks, digits, or whitespace will be included within each word.
Output
A single line of output should be generated for each string. The line should include “#”, followed by the problem number, followed by a colon and a space, followed by the string “YES” or “NO”.
Sample Input
ABCCBA A HELLO ABAB AMA ABAB PPA STOP
Sample Output
#1: YES #2: YES #3: NO #4: NO #5: YES #6: NO #7: NO
这道题目不上代码了,具体想放出来的原因是,在While( !EOF)里面的函数体,我定义了char a[] ,它每次的任务是负责装一个字符串,但是循环后我发现会保留上个案例的答案在字符串里面,这是没有清空字符串 ,而用char定义的字符串如何清空呢,用 memset(s,'\0',sizeof s)
而用 string定义的可以 s.clear(),也可以 s="";
并且 s+=a[] ,是字符串的增加
还有 char a[N] string s 也可以 if(s==a)
汉诺塔问题 HDOJ 2175 汉诺塔IX
Problem Description
1,2,...,n表示n个盘子.数字大盘子就大.n个盘子放在第1根柱子上.大盘不能放在小盘上.
在第1根柱子上的盘子是a[1],a[2],...,a[n]. a[1]=n,a[2]=n-1,...,a[n]=1.即a[1]是最下
面的盘子.把n个盘子移动到第3根柱子.每次只能移动1个盘子,且大盘不能放在小盘上.
问第m次移动的是那一个盘子.
Input
每行2个整数n (1 ≤ n ≤ 63) ,m≤ 2^n-1.n=m=0退出
Output
输出第m次移动的盘子的号数.
Sample Input
63 1 63 2 0 0
Sample Output
1 2
题解
第一种方法 递归
为了防止以后的训练赛像这次一样想不出名堂,还是要一步一步理解汉诺塔问题
假设n是盘子的数量
1.n=1时
第一次--------1号盘-------- A------>C
2.n=2时
第一次--------1号盘-------- A------->B
第二次--------2号盘-------- A------->C
第三次--------1号盘-------- B------->C
:
:
:
综上可知我们可以用 n=2次来模拟n>=2的情况
第一阶段--------n-1个盘-------- A------->B
第二阶段--------第n号盘-------- A------->C
第三阶段--------n-1个盘-------- B------->C
一共的移动次数是 sum=2^n-1次
问题规模是n
第一阶段的移动次数是 2^(n-1)-1次
第二阶段的移动次数是 1次
第三阶段的移动次数是 2^(n-1)-1次
所以对于输入一个m
如果m<=2^(n-1)-1,说明在第一阶段
如果m==2^(n-1)-1+1,说明在第二阶段
如果m>2^ (n-1)-1+1 说明在第三阶段
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdio>
using namespace std;
const int N=64;
__int64 f[N];
int flag;
void dfs(int a,int b,int c,int n,__int64 m)
{
if(flag)
return;
if(!n)
return;
if(f[n-1]==m)
{
cout<<n<<endl;
flag=1;
return;
}
if(f[n-1]>m) //中间值大于m 说明处于第一阶段
dfs(a,c,b,n-1,m);
if(f[n-1]<m)
dfs(b,a,c,n-1,m-f[n-1]); //中间值小于m 说明处于第三阶段
}
int main()
{
f[0]=1;
f[1]=2;
for(int i=2;i<=N;i++)
{
f[i]=f[i-1]*2;
//cout<<f[i]<<endl;
}
__int64 m;
int n;
while(scanf("%d%I64d",&n,&m)!=EOF&&n)
{
flag=0;
int a=1,b=1,c=1;
dfs(a,b,c,n,m); //盘数和第m次移动一并放入递归
}
return 0;
}
第二种方法找规律
1.当n=1时
1
2.当n=2时
1 2 1
3.当n=3时
1 2 1 3 1 2 1
4当n=4时
1 2 1 3 1 2 1 4 1 2 1 3 1 2 1 (可以观察到新来的4的位置是3的位置的两倍)
5.当n=5时
1 2 1 3 1 2 1 4 1 2 1 3 1 2 1 5 1 2 1 3 1 2 1 4 1 2 1 3 1 2 1
:
:
:
规律由此出来了
既然具有对称性,那么一定与数字2有关,
可知,没有经过对称变换的数字的位置
数字 1 2 3 4 5
位置 1 2 4 8 16
当n为总盘数时,由此可以得出:
n=1时,数字1在2^(n-1)的位置上
n=2时,数字2在2^(n-1)的位置上
……
1 2 1 3 1 2 1 4 1 2 1 3 1 2 1 5 1 2 1 3 1 2 1 4 1 2 1 3 1 2 1
除了加粗部分
其他1的位置
3,5,7,9,11,13,1517……
其他2的位置
6,10,14,18,22……
其他3的位置
12,20,28……
可以看出呈现两倍的关系
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
int m,n;
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0)
break;
if(m%2!=0)
{
cout<<"1"<<endl; //如果m是奇数,那么输出1
continue;
}
else
{
int i=0;
while(m%2==0) //把它(偶数)变为一个奇数要几次,
//这个几次再加1就是对应的数字
{
m/=2;
i++;
}
cout<<i+1<<endl;
}
}
}
取(m堆)石子游戏
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 6247 Accepted Submission(s): 3823
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
using namespace std;
const int N=2e5+10;
int m;
int f[N];
int main()
{
while(scanf("%d",&m)!=EOF&&m)
{
int sum=0;
for(int i=1;i<=m;i++)
{
cin>>f[i];
sum^=f[i];
}
if(!sum)
puts("No");
else
{
puts("Yes");
int res;
for(int i=1;i<=m;i++)
{
res=f[i]^sum;
if(res<f[i])
{
cout<<f[i]<<" "<<res<<endl;
}
}
}
memset(f,0,sizeof f);
}
}
这道题有个经验就是 异或值不能直接用,要先把它转化为int 值