题型:小贪心
题意:
主角有两种干净的餐具,1号餐具m个,2号k个
他每天用一个盘子吃一种套餐,而且用了不洗,而是等到没盘子用了才会洗一个,
他吃1号餐时必须用1号餐具,但是吃二号餐时可以用1号或2号餐具。
现在给一个序列,第i个数代表第i天他会吃x号餐,
问它至少洗多少次盘子
思路:
小模拟
代码:
#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
#define eps 1e-14
using namespace std;
typedef long long LL;
const int MAXN = 1000000+100;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;
int a[1000+10];
int main()
{
int n,m,k;
cin >> n >> m >> k;
int cnt=0;
for(int i = 0 ; i < n; i++)
{
int a;
cin >> a;
if(a == 1) m--;
else
{
if(k >0)
k--;
else if(m > 0)
m--;
else
k--;
}
if(m < 0)
m=0,cnt++;
if(k < 0)
k=0,cnt++;
}
cout << cnt <<endl;
return 0;
}
题型:数学
题意:
有n个数,总和sall,前k大的数和为sk,每个数不小于l,不大于r , 求这个序列
思路:
采取的策略是:
前k个数,从sk/k加起,一直加到sk加完,后n-k个数从(sall-sk)/(n-k)加起,直到sall加完
可以发现采取这样的策略的话,这题的不小于l和不大于r的条件完全可以无视,因为情况必定存在,所以这个条件必定成立(以前k大的数为例,若出现小于l的,说明补完sk之后必定还是存在小于l的数,矛盾,大于的类似)
注意n-k为0要特判,因为这个被别人黑了一次
代码:
特判比较挫,不想改了
#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
#define eps 1e-14
using namespace std;
typedef long long LL;
const int MAXN = 1000000+100;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;
int main()
{
int n,k,l,r,sall,sk;
scanf("%d%d%d%d%d%d",&n,&k,&l,&r,&sall,&sk);
if(n == k)
{
int ave = sall/n;
sall -= ave*n;
for(int i = 0 ; i < n ; i++)
if(i)
printf(" %d" , ave + (--sall >= 0));
else
printf("%d" , ave + (--sall >= 0));
puts("");
return 0;
}
int ave = sk/k;
int tmp = sall-sk;
sk -= ave*k;
int aave = tmp/(n-k);
tmp -= aave*(n-k);
for(int i = 0 ; i < n ; i++)
{
if(i < k)
printf("%d " , ave + (--sk >= 0));
else
{
if(i!=n-1)
printf("%d " , aave + (--tmp>=0));
else
printf("%d\n" , aave + (--tmp >=0));
}
}
return 0;
}
题型:图论,多叉树,dfs
题意:
给一颗多叉树(顶点数为n,边数为n-1的无向图),一行三个数表示相邻的点和权值
权值为1表示这条边不用修理,2表示要修理
现在从1节点开始通向各个点,如果到达一个点之前有需要修理的边,就把这个点归进一个集合
输出最小集合
思路:
如果1-i,i-n之间有需要修的路,压入的是n而不是i,最基本的贪心
一个点之后如果没有需要修理的路,并且这个点和上一个点的路恰好需要修理,那么这个点一定是要加入集合的
问题就是如何确定这样的点:
方法就是回溯,用一个res += dfs()记录接下来的边状态,dfs函数:如果后续没有节点或者没有需要修理的路,就返回0,
这样如果res为0的话,说明回溯上来的路径中没有需要修的边 , 若同时权值w == 2的话,这个点就被加入了
代码如下:
//#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "vector"
#include "map"
#include "stack"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
const double eps = 1e-10;
struct edge
{
int now;
int next;
int w;
}e[300000+100];
int n;
int head[100000+100];
int vis[100000+100];
int cnt=0;
vector<int> out;
void add(int x , int y , int v)
{
e[cnt].now = y;
e[cnt].next = head[x];
e[cnt].w = v;
head[x] = cnt++;
}
int dfs(int now , int w)
{
vis[now] = 1;
int res=0;
for(int i = head[now] ; i != -1 ; i = e[i].next)
{
int next = e[i].now;
if(!vis[next])
res += dfs(next , e[i].w);
}
if(!res && w == 2)
{
out.push_back(now);
return 1;
}
return res;
}
int main()
{
cin >> n;
memset(head,-1,sizeof(head));
for(int i = 1 ; i < n ; i++)
{
int a,b,c;
cin >> a >> b >> c;
add(a,b,c);
add(b,a,c);
}
dfs(1,1);
cout << out.size() << endl;
for(int i = 0 ; i < out.size() ; i++)
if(i)
cout << ' ' << out[i] ;
else
cout << out[i] ;
return 0;
}