南昌理工acm
动态规划入门
刷了一些题,举俩例子:
(1) 超级楼梯(杭电 2041)
杭电 2041
*Problem Description
有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第M级,共有多少种走法?
Input
输入数据首先包含一个整数N,表示测试实例的个数,然后是N行数据,每行包含一个整数M(1<=M<=40),表示楼梯的级数。
Output
对于每个测试实例,请输出不同走法的数量
Sample Input
2
2
3
Sample Output
1
2*
这类问题可以用斐波那契(Fibonacci)数列进行
//dp数组,用以保存已经计算过的结果
//dp[n]记录F(n)的结果,dp[n]= -1表示没有计算过
int fib(int n){
if ( n == 1 || n==2 ) return 1;
if ( dp[n] != -1 ) return dp[n];
else {
dp[n] = fib(n-1) + fib(n-2);
return dp[n];
}
}
本题AC代码:
#include<stdio.h>
int a[41];
int fun(int i);
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
int m,f;
scanf("%d",&m);
f=fun(m);
printf("%d\n",f);
}
}
int fun(int i)
{
a[41]={0};
if(i==1||i==2) return 1;
if(a[i]!=0) return a[i];
else{
a[i]=fun(i-1)+fun(i-2);
}
return a[i];
}
(2)数塔(杭电 2084)
杭电 2084
*在讲述DP算法的时候,一个经典的例子就是数塔问题,它是这样描述的:
有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?
已经告诉你了,这是个DP的题目,你能AC吗?
Input
输入数据首先包括一个整数C,表示测试实例的个数,每个测试实例的第一行是一个整数N(1 <= N <= 100),表示数塔的高度,接下来用N行数字表示数塔,其中第i行有个i个整数,且所有的整数均在区间[0,99]内。
Output
对于每个测试实例,输出可能得到的最大和,每个实例的输出占一行。
Sample Input
1
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Sample Output
30*
这是一经典的动态规划问题
做题方法有 递推 递归 记忆化搜索
递推:
void solve ()
{
int i , j;
for(j = 1; j <= n; j ++) d[n][j ] = a[n][j];
for(i = n -1; i >= 1; i - -)
for(j = 1; j <= i; j ++)
d[i][j ] = a[i][j ] + max (d[i +1][ j], d[i +1][ j +1]);
}
时间复杂度为:时间复杂度O(n^2)
递归:
int solve ( int i , int j)
{
if (i == n) return a[i][j]; else
return a[i][j] + max( solve (i+1,j), solve (i+1 , j +1));
}
太多重复运算
记忆化搜索:
// initially , all d[i][j] are -1
int solve ( int i , int j)
{
if( i == n ) return a[i][j];
if(d[ i ][ j ] >= 0) return d[i][j];
d[i][j] = a[i][j]+max(solve ( i+1, j ), solve ( i+1 , j +1) );
return d[i][j];
}
时间复杂度:时间复杂度O(n^2)
不必事先确定各状态的计算顺序
AC代码(递推)
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int c,n,i,j,k;
int dp[101][101],a[101][101];
cin>>c;
while(c--)
{
cin>>n;
for(i=0;i<=n;i++)
{
for(j=1;j<=i;j++)
{
cin>>a[i][j];
}
}
for(j=1;j<=n;j++) dp[n][j]=a[n][j];
for(i=n-1;i>=1;i--)
{
for(j=1;j<=i;j++)
{
dp[i][j]=a[i][j]+max(dp[i+1][j],dp[i+1][j+1]);
}
}
cout<<dp[1][1]<<endl;
}
}
背包
01背包模板
还没做题,刚搞懂模板
#include<bits/stdc++.h>
using namespace std;
int f[100];//背包容积
int V;//背包容积
int n;//物品数
int w[100],c[100];//物品价值&体积
int work()
{
for(int i=1;i<=n;++i)
for(int v=V;v>=c[i];--v)
f[v]=max(f[v],f[v-c[i]]+w[i]);
return f[V];
}
int main()
{
}
百度之星
另外搞懂了一些比赛的题目,以百度之星为例
拿周日(7月26号初赛三)的一题举个例子
1003 Permutation
1003
Problem Description
一开始有 n 个数,他们按 1…n1…n 的顺序排列,要求交换最多 m 对数字(同一个数字可以参与多次交换),使得逆序对数目最大。对于一个序列 A,如果存在正整数 i, j使得 1≤i<j≤n 而且 A[i]>A[j],则 <A[i],A[j]> 这个有序对称为 A的一个逆序对。
Input
第一行一个正整数 )test (1≤test≤100000) 表示数据组数。
对于每组数据,一行两个整数n,m (1≤n≤1000000,0≤m≤1000000) 表示数字个数和最多可以交换的数字对数。
Output
对于每组数据,一行一个整数表示答案。
Sample Input
6
1 1
2 0
2 1
3 1
4 1
4 2
Sample Output
0
0
1
3
5
6
解题方法:
这一题不能直接根据题意设数组去做,需要找公式规则
当 m≥⌊n/2⌋ 时,一定能变成 n…1,这时逆序对数最大。否则我们依次交换 1 和 n,2 和 n-1,…,一共交换 m 对。
(1) 直接设数组做,会超空间,无论数据开多大都会超空间
会出现:
Runtime Error
(ACCESS_VIOLATION)
#include<iostream>
using namespace std;
const long long maxn=1000000000000001;
long long test,n,m;
long long a[maxn];
int main()
{
long long i,j,k;
cin>>test;
while(test--)
{
cin>>n>>m;
for(i=0;i<n;i++)
{
a[i]=i+1;
}
if(n>1)
{
for(i=0,j=n-1;i<=m-1;i++,j--)
{
k=a[i];
a[i]=a[j];
a[j]=k;
}
}
long long count = 0;
for(i=0;i<n-1;i++)
{
for(j=i+1;j<n;j++)
if(a[i]>a[j]) count+= 1;
}
cout<<count<<endl;
}
}
AC代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
int n, m;
ll cal(ll x) {
return x * (x + 1) / 2;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
if (m >= n / 2) printf("%lld\n", cal(n - 1));
else {
ll ans = cal(n - 1) - cal(n - 1 - m) + cal(m - 1);
ans += 1LL * (n - m * 2) * m;
printf("%lld\n", ans);
}
}
return 0;
}
这一周还刷了不少水题,就不举例了