计数问题
题意 :0-n之间有多少个k
思路
例如求:abcdefg中k的个数
- 枚举带第d时候
- abc d efg中k的数
- 开始分类讨论,拿abc说事
- 000-abc-1 d 000-efg
- abc固定
- d<k 0
- d==k. 000-efg
- d>k 000-999
- 怎么处理前导0,
- 前导0的出现:k=0时才会影响以上步骤
- 如果k==0,枚举的时候从次高位枚举,不会出现00了
- 此外,分类时候,000-abc-1 d 000-efg这种情况,d==0,000-abc-1不能为0
代码
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define mst(s,_s) memset(s, _s, sizeof(s))
const double PI = acos(-1.0);
const double eps = 1e-6;
const int INF = 0x3f3f3f3f;
const int N = 1e6+100;
int T,n,m;
int get_num(vector<int>nums,int l,int r)
{
int res=0;
for(int i=l;i>=r;i--)
res=res*10+nums[i];
return res;
}
int get10(int x)
{
int res=1;
while(x--)
{
res*=10;
}
return res;
}
// 1到n之间,有多少x
int dp(int n,int k)
{
if(!n) return 0;
vector<int>nums;
while(n){
nums.push_back(n%10);
n/=10;
}
int res=0;
/* abcxedg. 拿abc的大小shiu
0 - abc-1 x 000-999
abc
x<k 0
x==k 000 edg
x>k 000-999
当k=0
abc不能为0,否则0 +0 前导0
*/
n=nums.size();
for(int i=n-1-!k;i>=0;i--)
{
int x=nums[i];
if(i<n-1)
{
res+=get_num(nums,n-1,i+1)*get10(i);
if(k==0) res-=get10(i);
}
if(x>k) res+=get10(i);
else if(x==k) res+=1+get_num(nums,i-1,0);
}
return res;
}
int main() {
int l,r;
while(cin>>l>>r)
{
if(!l || !r) break;
if(l>r) swap(l,r);
for(int i=0;i<=9;i++)
cout<<dp(r,i)-dp(l-1,i)<<' ';
cout<<endl;
}
return 0;
}
没有上司的舞会
树形DP f[u] [0] 表示u为根节点不选u的最大值,f[u] [1] 表示u为根节点选u的最大值
选u的话,各个子节点只能不选,不选u的话,子节点可选可不选
代码
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 6010;
int n;
int h[N], e[N], ne[N], idx;
int happy[N];
int f[N][2];
bool has_fa[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void dfs(int u)
{
f[u][1] = happy[u];
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
dfs(j);
f[u][1] += f[j][0];
f[u][0] += max(f[j][0], f[j][1]);
}
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d", &happy[i]);
memset(h, -1, sizeof h);
for (int i = 0; i < n - 1; i ++ )
{
int a, b;
scanf("%d%d", &a, &b);
add(b, a);
has_fa[a] = true;
}
int root = 1;
while (has_fa[root]) root ++ ;
dfs(root);
printf("%d\n", max(f[root][0], f[root][1]));
return 0;
}
滑雪
一个二维矩阵,代表一个平面的高度,从高往低滑
问:最长的滑雪路径。
f[i] [j] 表示从这个点开始滑的最长距离
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e3+100;
int f[N][N],h[N][N];
int n,m;
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
int dp(int a,int b)
{
if(f[a][b]!=-1) return f[a][b];
int maxv=1;
for(int i=0;i<4;i++)
{
/// cout<<ssss<<endl;
int na=a+dx[i],nb=b+dy[i];
//cout<<na<<' '<<nb<<endl;
if(na>=1 && na<=n && nb>=1 && nb<=m && h[na][nb] < h[a][b])
maxv = max(maxv,dp(na,nb)+1);
}
return f[a][b]=maxv;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>h[i][j];
int res=0;
memset(f,-1,sizeof f);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
// cout<<i<<' '<<j<<endl;
res=max(res,dp(i,j));
}
cout<<res<<endl;
return 0;
}
最长上升子序列(贪心)
代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n;
int a[N];
int q[N];
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
int len = 0;
for (int i = 0; i < n; i ++ )
{
int l = 0, r = len;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (q[mid] < a[i]) l = mid;
else r = mid - 1;
}
len = max(len, r + 1);
q[r + 1] = a[i];
}
printf("%d\n", len);
return 0;
}