2013-12
最も頻繁に発生する番号
解決策: この質問では、統計にハッシュ テーブルを使用しています
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
using namespace std;
const int maxn = 10010;
int hashtable[maxn] = {
0 };
int main() {
int n;
int i;
int num;
scanf("%d", &n);
for (i = 0; i < n; i++)
{
scanf("%d",&num);
hashtable[num]++;
}
int max = 0;
for (i = 0; i < maxn; i++)
{
if (hashtable[i] > hashtable[max])
max = i;
}
printf("%d",max);
return 0;
}
ISBN番号
解決策: 通常の文字列処理、大きな文字列に注意してください
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
using namespace std;
int main() {
char a[15];
scanf("%s",a);
int num=0;//计算正确的识别码
int temp = 1;
for (int i = 0; i <= 10; i++)
{
if (i != 1 && i != 5)
{
num += (a[i] - '0') * temp;
temp++;
}
}
num %= 11;
if (num != 10)
{
if (num == a[12] - '0')
{
printf("Right");
}
else
{
a[12] = num + '0';
printf("%s", a);
}
}
else
{
if (a[12] == 'X')
{
printf("Right");
}
else
{
a[12] ='X';
printf("%s", a);
}
}
return 0;
}
最大の長方形
解決策: この質問では、
動的計画法で合計 n 個の四角形を調べます。S[i][j] は、座標 i と座標 j の間の最大の四角形領域を表します。座標 i は 1 から始まり n まで続き、j は i+ から始まることに注意してください。 1 は n+1 で始まり、n+1 で終わる;
初期条件: S[i][i+1]=h[i]
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdio.h>
#include<string>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<limits>
using namespace std;
//最大的矩形
const int maxn=1010;
int S[maxn][maxn];//S[i][j]表示下标i到下标j之间的最大矩形面积
int h[maxn];//h[1]-h[n]
int main()
{
int i, j;
int n;
scanf("%d",&n);
for (int i = 1; i <= n; i++)
{
scanf("%d",&h[i]);
}
int minh;
int maxlen;
for (int i = 1; i <= n; i++)
{
minh = h[i];
maxlen = 1;
S[i][i + 1] = h[i];
for (int j = i+2; j <= n+1; j++)
{
if (h[j-1] >= minh)
{
maxlen++;
S[i][j] = minh * maxlen;
}
else
{
minh = h[j-1];
maxlen++;
S[i][j] = max(S[i][j-1], minh * maxlen);
}
}
}
int maxS = -1;
for (int i = 1; i <= n; i++)
{
for (int j = i + 1; j <= n+1; j++)
{
if (S[i][j] > maxS)
maxS = S[i][j];
}
}
printf("%d",maxS);
return 0;
}
興味深い数
解決策: この質問はまだ動的計画法に関するものです. Duck
status[i][j] は長さが i で状態が j の興味深い数を意味します.
興味深い数には 5 つの状態があることが知られています.
文字列全体では 1 つだけです.文字: 質問から この文字は 2 しかないことを知っておいてください. 状態
0: 2 あります
文字列全体には 2 つの文字があります:
状態 1: 2 があります, 0
状態 2: 2, 3 があります; そして 3 は 2, の後ろにあります
文字列全体で 3 文字
状態 3 : 2、0、1 があり、1 が 0 の後ろにある
状態 4: 2、3、0 があり、3 が 2 の後ろにある
文字列全体が 4 文字である:
状態 5: ある2, 3, 0, 1; 2 の後に 3、0 の後に 1
初期条件は次のように知ることができます
。
dp[1][0]=1;
dp[1][1]=0;
dp[1][2]=0;
dp[1][3]=0;
dp[1][4]=0;
dp[1][5]=0;
次に、i を 2 から n まで反復し、最終的に dp[i][5] が結果になります。反復式は次のとおりです。
dp[i][0]=1;//显而易见
//只有2,长度为i-1的字符串在后面加上0
//有2,0,长度为i-1的字符串在后面加上2或则和0,所以要*2
dp[i][1]=dp[i-1][0]+2*dp[i-1][1];
//只有2,长度为i-1的字符串在后面加上3
//有2,3,长度为i-1的字符串,因为2只能在3前面,所以只能在i-1字符串后面加上3
dp[i][2]=dp[i-1][0]+dp[i-1][2];
//只有2、0,长度为i-1的字符串在后面加上1
//有2,0,1,长度为i-1的字符串,面加上2或者1
dp[i][3]=dp[i-1][1]+dp[i-1][3]*2;
//有2,0,长度为i-1的字符串在后面加上3
//有2,3,长度为i-1的字符串在后面加上0
//有2,0,3的字符串在后面加上3或者0
dp[i][4]=dp[i-1][1]+dp[i-1][2]+dp[i-1][4]*2;
//有2、0、1、3的字符串在后面加上1或者3
//和上面一样,类推
//和上面一样,类推
dp[i][5]=dp[i-1][5]*2+dp[i-1][3]+dp[i-1][4];
MOD=1000000007 を法とする必要があるため
、dp のすべての計算は %MOD です。
同時に、dp 配列が long long に開かれていることに注意してください。
/* CCF201312-4 有趣的数 */
#include <iostream>
#include <cstring>
using namespace std;
const long long MOD = 1000000007;
const int MAXN = 1000;
const int MAXS = 5;
long long status[MAXN+1][MAXS+1];
int main()
{
int n;
cin >> n;
memset(status, 0, sizeof(status));
// DP
status[1][0] = 1;
for(int i=2; i<=n; i++) {
status[i][0] = 1;
status[i][1] = (status[i - 1][1] * 2 + status[i - 1] [0]) % MOD;
status[i][2] = (status[i - 1][2] + status[i - 1][0]) % MOD;
status[i][3] = (status[i - 1][3] * 2 + status[i - 1][1] ) % MOD;
status[i][4] = (status[i - 1][4] * 2 + status[i - 1][1] + status[i - 1][2]) % MOD;
status[i][5] = (status[i - 1][5] * 2 + status[i - 1][3] + status[i - 1][4]) % MOD;
}
cout << status[n][5] << endl;
return 0;
}
行き詰まった! ⭐
問題解決: この問題の現在の考え方は、最初に BFS 幅で 2 回検索し、記録用の配列を開き、最後に結合することです。
2014 年 3 月
反対の数
解決策:同じハッシュテーブル数
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
using namespace std;
const int maxn = 1010;
int hashtable[maxn] = {
0};
int main() {
int n, i;
int num ;
scanf("%d",&n);
for (i = 0; i < n; i++)
{
scanf("%d", &num);
hashtable[abs(num)]++;
}
num = 0;//表示一共有多少对
for (i = 0; i < maxn; i++)
{
if (hashtable[i] == 2)
num++;
}
printf("%d",num);
return 0;
}
窓
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
using namespace std;
struct Window
{
int cengci;//cengci=n说明在最上面
int id;//id表示编号
int x1;
int x2;
int y1;
int y2;
}win[12];//最多10个窗口
bool cmp(Window a, Window b)
{
return a.cengci < b.cengci;
}
int main() {
int n, m;
int i;
int tempx, tempy;
int j;
scanf("%d %d", &n, &m);
for (i = 1; i <=n; i++)
{
scanf("%d %d %d %d", &win[i].x1, &win[i].y1, &win[i].x2, &win[i].y2);
win[i].id = i;
win[i].cengci = i;//
}
int tempcengci;
int tempid;
for (i = 1; i <= m; i++)
{
tempcengci = 0;
sort(win+1, win +1+ n, cmp);//注意sort是怎么写的
scanf("%d %d",&tempx,&tempy);
for (j = n; j >=1; j--)//从cengci由大到小,即从最上面开始选
{
if (win[j].x1 <= tempx && win[j].y1 <= tempy && win[j].x2 >= tempx && win[j].y2 >= tempy)
{
printf("%d\n",win[j].id);//如果选到了,就把之前的id输出
tempcengci = win[j].cengci;
tempid = win[j].id;
win[j].cengci = n;//排在最前面
break;
}
}
if (tempcengci == 0)
{
printf("IGNORED\n");
}
else
{
for (j = n; j >= 1; j--)//从cengci由大到小,即从最上面开始选
{
if(win[j].cengci > tempcengci&&win[j].id!=tempid)
win[j].cengci--;
}
}
}
return 0;
}
コマンド ライン オプション
この設問は1回目20点、2回目40点満点で、参考までに満点をとります。
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
#include<map>
#include<vector>
//这道题目有几个很重要的思想:
/*
* 1、将输入的一行string字符按照空格分开单独存储在vector<string>中
用vector的迭代器it来取vecotr里面的string
模板如下:
for (pos = line.find(" "); pos != -1; pos = line.find(" "))
{
ve.push_back(line.substr(0,pos));
line = line.substr(pos+1);
}
ve.push_back(line);
* 2、题目说按照升序输出,并且后出现的参数可以覆盖前面的参数,map可以完美实现这个要求
* ①map自动按照key进行升序排序
* ②map的key对应唯一值,可以实现后来覆盖
*
* 3、不用急着将命令存到某个数据结构里面,就让它们在str里面呆着
* 用的时候使用find来判断是不是命令,以及带不带参数
*
* 4、要注意进行边界检查
*/
using namespace std;
string str;
string str1;
string line;
int n;
int pos;
vector<string> ve;
map<char, string> ans;//最终的映射结果,map会以键值从小到大自动排序
int main() {
cin >> str;
cin >> n;
getline(cin,str1);//吸收换行符
for (int i = 1; i <= n; i++)
{
ve.clear();
ans.clear();
getline(cin,line);//输入Line
//第一步.将每一行根据空格存储在vector ve中
for (pos = line.find(" "); pos != -1; pos = line.find(" "))
{
ve.push_back(line.substr(0,pos));
line = line.substr(pos+1);
}
ve.push_back(line);
/*第二步.判断数据是否合法,不合法直接break
合法则装入map
若有参数,则装入参数
*/
vector<string>::iterator it;
for (it = ve.begin()+1; it != ve.end(); it++)
{
string ss = *it;//相当于取到了vector中的字符串
if (ss[0] == '-' && str.find(ss[1]) != -1)//是命令
{
pos = str.find(ss[1]);
if (pos+1<str.length()&&str[pos + 1] == ':')//说明是带参数的
{
if (it + 1 != ve.end())
{
it += 1;
string paramter = *it;
ans[ss[1]] = paramter;
}
}
else//是不带参数的
{
ans[ss[1]] = "!";
}
}
else//不是命令,直接break;
break;
}
cout << "Case " << i << ":";
map<char, string>::iterator iter;
for (iter = ans.begin(); iter != ans.end(); iter++)
{
if (iter->second == "!")
{
cout << " -" << iter->first;
}
else
cout << " -"<<iter->first <<" "<< iter->second;
}
cout << endl;
}
return 0;
}
無線ネットワーク
BFS 深さ優先検索には多くの用途があります
1. 迷路問題で使用されます
2. 各レイヤーをトラバースして最小ステップ数を見つけるために使用されます (原則は、見つかったらすぐに戻ることであり、返されるレイヤーの数は一番小さい)
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
//无线网络
//思路是广度优先搜索,因为是广度优先,
//一旦到达第二个路由器就立即返回中转路由器个数,所以可以确定返回地就是最少的中转个数
using namespace std;
int n, m, k;
long long r;//n个无线路由器,m个拜访无线路由器的位置,选择最多k个位置,两无线路由器距离r
const int maxn = 210;
struct Luyou {
long long x, y;
int step;
}L[maxn];//最多210个路由器
int inq[maxn] = {
0};//表示有没有入过队
int sum;//总的个数
int judge(long long x1, long long y1,long long x2,long long y2)//可以到达返回1,否则返回0
{
if ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) <= r * r)
return 1;
else
return 0;
}
int BFS()
{
queue<Luyou> Q;
Q.push(L[0]);//将起点push进去
inq[0] = 1;
while (!Q.empty())
{
Luyou top = Q.front();
Q.pop();
if (top.x == L[1].x && top.y == L[1].y)
return top.step - 1;
else
{
for (int i = 0; i < sum; i++)
{
if (inq[i]) continue;
if (judge(top.x, top.y, L[i].x, L[i].y))//如果可以
{
inq[i] = 1;
Q.push({
L[i].x,L[i].y,top.step + 1 });
}
else
continue;
}
}
}
}
int main()
{
scanf("%d %d %d %lld",&n,&m,&k,&r);//
sum = n + m;
for (int i = 0; i < sum; i++)
{
scanf("%lld %lld",&L[i].x,&L[i].y);
L[i].step = 0;//初始化操作
}
printf("%d",BFS());
return 0;
}
タスクスケジューリング⭐
謙虚なSOS
2014 年 9 月
隣接ペア
基本的な質問、ただの統計
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
using namespace std;
const int maxn = 1010;
int a[maxn];
int main() {
int n, i;
int num = 0;
scanf("%d",&n);
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
sort(a,a + n);
for (i = 0; i < n-1; i++)
{
if (a[i] == a[i + 1] - 1)
num++;
}
printf("%d",num);
return 0;
}
描く
解決策:アイデアもハッシュテーブルで、写真の各ブロックの色を左下隅のポイントの価格として設定し、ハッシュテーブル[10000]を使用して2次元の100 * 100座標を格納します
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
using namespace std;
const int maxn = 10010;
int hashtable[maxn] = {
0};
int main() {
int n, i,j,k;
int num = 0;
int x1, y1, x2, y2;
scanf("%d",&n);
for (i = 0; i < n; i++)
{
scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
x2--; y2--;
for(j=x1;j<=x2;j++)
for (k = y1; k <= y2; k++)
{
hashtable[j * 100 + k] = 1;
}
}
for (i = 0; i < maxn; i++)
{
num += hashtable[i];
}
printf("%d",num);
return 0;
}
文字列一致
解決策: 元の文字列 S が出力されるため、
次のようにいくつかの便利なライブラリ関数を保存することを忘れないでください。
#include<ctype.h>
isdigit(i)//判断是否是十进制数字,如果是数字,返回非0值,否则返回0
isalpha(i)//判断是否是字母
isalnum(i)//判断是否是字母或者数字
toupper(i)//将i变成大写字母
tolower(i)//将i变成小写字母
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstring>
#include <map>
#include <vector>
#include<string>
using namespace std;
string str;
string S;
string temp;
string func(string S)//功能:将字符串S都变成大写
{
for (int i = 0; i < S.length(); i++)
{
S[i] = toupper(S[i]);
}
return S;
}
int main()
{
getline(cin,S);
int flag;
int n;
scanf("%d",&flag);
scanf("%d",&n);
getchar();//注意吸收换行符
if (flag == 1)//表示大小写敏感
{
for (int i = 0; i < n; i++)
{
str.clear();
getline(cin, str);
if (str.find(S) != -1)//说明找到了子字符串
cout << str<<endl;
}
}
else//表示大小写不敏感
{
//将S都变成大写
S = func(S);
for (int i = 0; i < n; i++)
{
str.clear();
getline(cin, str);
temp = func(str);
if (temp.find(S) != -1)//说明找到了子字符串
cout << str << endl;
}
}
}
最適な食事のマッチング
BFS を一目見てわかるように、BFS は同時に検索して、同じ住所からの注文を組み合わせることができるということです。
タイムアウトはわずか40分なので、すべての始点を一度にBFSの初期キューに入れ、配達待ちの顧客数が0人になったら、配達が終了したことを意味し、戻ることができます。
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<limits>
using namespace std;
//最优配餐-一眼BFS
int n, m, k, d;//方格图大小,分店数量,客户数量,不能经过的点的数量
const int maxn = 1010;//存储分店的地址
struct node {
int x, y;
int step;
node() {
};
node(int xx, int yy, int sstep) {
x = xx, y = yy, step = sstep; }//初始化操作
}Node[maxn];
int kehu[maxn][maxn];
int kehucount=0;
long long ans=0;
int tempx, tempy,tempm;
int inq[maxn][maxn];//没有入队就是0,如果已经入队,或者不可以经过就设置为1,把每个店的地址也设置为1
int X[4] = {
0,0,1,-1 };
int Y[4] = {
1,-1,0,0 };
queue<node> q;
int judge(int x, int y)
{
if (x < 1 || x > n || y < 1 || y > n) return 0;
if (inq[x][y] == 1) return 0;
return 1;
}
void BFS()
{
node top, cur;
while (!q.empty())
{
top = q.front();
q.pop();
//if (kehu[top.x][top.y] > 0)
//{
// ans += kehu[top.x][top.y] * top.step;//因为如果就在店里配送费为0,所以不用考虑这种情况
//}
for (int i = 0; i < 4; i++)
{
cur.x = top.x + X[i];
cur.y = top.y + Y[i];
cur.step = top.step + 1;
if (judge(cur.x, cur.y))//如果符和要求
{
if (kehu[cur.x][cur.y] > 0)
{
ans += kehu[cur.x][cur.y] * cur.step;
kehucount--;
if (kehucount == 0)
return;
}
inq[cur.x][cur.y] = 1;
q.push(cur);
}
}
}
}
int main()
{
//首先进行初始化
fill(kehu[0],kehu[0]+maxn*maxn,0);//各地订单为0
fill(inq[0], inq[0] + maxn * maxn, 0);
scanf("%d %d %d %d",&n,&m,&k,&d);
for (int i = 0; i < m; i++)
{
scanf("%d %d", &tempx, &tempy);
q.push(node(tempx,tempy,0));
inq[Node[i].x][Node[i].y] = 1;
}
for (int i = 0; i < k; i++)
{
scanf("%d %d %d",&tempx,&tempy,&tempm);//
if (kehu[tempx][tempy] == 0)
kehucount++;//客户所在地点的数量
kehu[tempx][tempy] += tempm;
}
for (int i = 0; i < d; i++)
{
scanf("%d %d", &tempx, &tempy);
inq[tempx][tempy] = 1;//说明不能访问
}
BFS();
printf("%lld\n",ans);
return 0;
}
パズル⭐
2014-12
アクセス制御システム
ハッシュ テーブル レコード
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
using namespace std;
const int maxn = 1010;
int a[1010] = {
0};//记录最多1000位读者出现的次数
int main() {
int n, i;
int id;
scanf("%d",&n);
for (i = 0; i < n; i++)
{
scanf("%d",&id);
a[id]++;
printf("%d ",a[id]);
}
return 0;
}
ジグザグスキャン
主なことは、状況に応じてこの問題を議論することです.
2つの主なポイントがあります: まず、ループ内の判断は、最初にnに等しいことを確認する必要があり、次にそれが1に等しいかどうかを確認する必要があります; 値は何ですか?変数の? i または j を添字の範囲外に出力するか、それとも次のサイクルの期待値外に出力するか?
サンプルは n=4 として与えられていることに注意してください. コードでは、j<4 を直接使用して、始まり はい、これは間違いです。10 点だけが理由かもしれません orz
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
using namespace std;
const int maxn = 510;
int a[510][510];
int main() {
int n, i,j;
scanf("%d",&n);
for (i = 1; i <= n; i++)
{
for (j = 1; j <= n; j++)
{
scanf("%d", &a[i][j]);
}
}
i = j = 1;
while (i + j <= 2 * n)
{
if (j == n) //左下
{
printf("%d ", a[i][j]);
i++;
while (i < n)
{
printf("%d ", a[i][j]);
i++;
j--;
}
continue;
}
if (i == n) //右上
{
printf("%d ", a[i][j]);
j++;
while (j < n)
{
printf("%d ", a[i][j]);
j++;
i--;
}
continue;
}
if (i == 1) //左下
{
printf("%d ", a[i][j]);
j++;
while (j > 1)
{
printf("%d ", a[i][j]);
i++;
j--;
}
continue;
}
if (j == 1)//右上
{
printf("%d ", a[i][j]);
i++;
while (i > 1)
{
printf("%d ", a[i][j]);
i--;
j++;
}
continue;
}
}
return 0;
}
コールオークション
操作エラーを表示する90ポイントのコード
も各行を処理するための文字列です. 選択されたデータ構造は構造体であり, 現在の操作の有効性を示すためにフラグが使用されます. フラグ=1の場合は購入を意味します, フラグ=2 flag =3 は初期状態を意味します。
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstring>
#include <map>
#include <vector>
#include<string>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 5010;
struct Buy {
int flag;
double p;
int s;
}bs[maxn];
priority_queue<double> q;//存储开盘价
int sum;
int realsum;
int sumb;
int sums;
char op[10];//操作
bool cmp(Buy a, Buy b)
{
if (a.flag != b.flag) return a.flag < b.flag;
else if (a.p != b.p) return a.p < b.p;
else return 1;
}
int main()
{
sum = 0;
realsum = 0;
sumb = sums = 0;
int temp;
for (int i = 0; i < maxn; i++)
{
bs[i].flag = 3;
}
while (scanf("%s",op) != EOF)
{
if (op[0] == 'b'||op[0]=='s')
{
sum++;
realsum++;
if (op[0] == 'b')
{
//printf("遇到了buy\n");
bs[sum].flag = 1;
sumb++;
}
else
{
//printf("遇到了sell\n");
bs[sum].flag = 2;
sums++;
}
scanf("%lf %d",&bs[sum].p,&bs[sum].s);
//printf("sum=%d,flag=%d,p=%.2f,s=%d\n", sum,bs[sum].flag, bs[sum].p, bs[sum].s);
//printf("sumb=%d,sums=%d\n",sumb,sums);
q.push(bs[sum].p);
}
else
{
scanf("%d", &temp);
if (bs[temp].flag == 1)
sumb--;
else
sums--;
bs[temp].flag =3;
realsum--;
sum++;
}
}
sort(bs+1, bs+1 + sum, cmp);
//1-sumb是买进,sumb+1到sumb+sums是卖出
/*for (int i = 1; i <= realsum; i++)
{
printf("flag=%d,p=%.2f,s=%d\n", bs[i].flag, bs[i].p, bs[i].s);
}*/
//printf("sumb=%d\n",sumb);
long long max=0;
long long tempmax;
double tempp=0;
double ans;
long long maxb;
long long maxs;
while (!q.empty())
{
maxb = maxs = 0;
if (tempp != q.top())
{
tempp = q.top();
q.pop();
int i;
for (i = 1; i <= realsum; i++)
{
if (bs[i].p >= tempp && i <= sumb)
{
maxb += bs[i].s;
}
if (bs[i].p <= tempp && i > sumb)
{
maxs += bs[i].s;
}
}
tempmax = min(maxb, maxs);
if (tempmax > max)
{
max = tempmax;
ans = tempp;
}
}
else
{
q.pop();
}
}
printf("%.2f %lld\n", ans, max);
}
最適な灌漑
この問題は最小全域木アルゴリズムです:
最小全域木アルゴリズムを使用する場合は、開始点が 0 から始まるか 1 から始まるかに注意してください。
Prim アルゴリズム -> 密なグラフ (複数の側面) に適しています
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<limits>
using namespace std;
//最优灌溉
const int maxv = 1010;
const int INF = 1000000000;//设置为一个很大的数
int n, m,G[maxv][maxv];
int d[maxv];
int vis[maxv] = {
0 };//标记第i个点有没有被访问
long long prim()
{
fill(d,d+maxv,INF);
d[1] = 0;
long long ans = 0;
for (int i = 1; i <= n; i++)
{
int u = -1, MIN = INF;
for (int j = 1; j <= n; j++)
{
if (vis[j] == 0 && d[j] < MIN)
{
u = j;
MIN = d[j];
}
}
if (u == -1) return -1;
vis[u] = 1;
ans += d[u];
for (int v = 1; v <= n; v++)
{
if (vis[v] == 0 && G[u][v] != INF && G[u][v] < d[v])
d[v] = G[u][v];
}
}
return ans;
}
int main()
{
int u, v, w;
scanf("%d %d",&n,&m);
fill(G[0],G[0]+maxv*maxv,INF);
for (int i = 0; i < m; i++)
{
scanf("%d %d %d",&u,&v,&w);
G[u][v] = G[v][u] = w;
}
long long ans = prim();
printf("%lld\n",ans);
return 0;
}
2. k アルゴリズム -> まばらなグラフ (エッジが少ない) に適しています
貨物発送
ZKW テンプレート コード 1
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<limits>
using namespace std;
#define IOS ios::sync_with_stdio(false)
#define maxn 805
#define maxm 50005
#define INF 200000005
#define ll long long
struct ZKW
{
int s, t, n;
int cost;
bool vis[maxn];
int dist[maxn];
//vis两个用处:spfa里的访问标记,増广时候的访问标记,dist是每个点的距离标号
int nedge, p[maxm], c[maxm], cc[maxm], nex[maxm], head[maxn];
//p[i]表示以某一点出发的编号为i的边对应点,c表示编号为i的边的流量,cc表示编号为i的边的费用
void addedge(int x, int y, int z, int zz)//x->y 流量 费用
{
p[++nedge] = y, c[nedge] = z, cc[nedge] = zz, nex[nedge] = head[x], head[x] = nedge;
p[++nedge] = x, c[nedge] = 0, cc[nedge] = -zz, nex[nedge] = head[y], head[y] = nedge;
}
void init(int n, int s, int t)//传入有多少个点,起点及终点
{
this->n = n, this->s = s, this->t = t;
memset(nex, -1, sizeof nex);
memset(head, -1, sizeof head);
cost = 0, nedge = -1;
}
bool spfa(int s, int t)
{
memset(vis, 0, sizeof vis);
for (int i = 0; i <= n; i++)dist[i] = INF;
dist[t] = 0;
vis[t] = 1;
deque<int>q;
q.push_back(t);
while (!q.empty())
{
int now = q.front();
q.pop_front();
for (int k = head[now]; k > -1; k = nex[k])if (c[k ^ 1] && dist[p[k]] > dist[now] - cc[k])
{
dist[p[k]] = dist[now] - cc[k];
if (!vis[p[k]])
{
vis[p[k]] = 1;
if (!q.empty() && dist[p[k]] < dist[q.front()])q.push_front(p[k]);
else q.push_back(p[k]);
}
}
vis[now] = 0;
}
return dist[s] < INF;
}
int dfs(int x, int low)
{
//这里就是进行増广了
if (x == t)
{
vis[t] = 1;
return low;
}
int used = 0, a;
vis[x] = 1;
//这边是不是和dinic很像啊
for (int k = head[x]; k > -1; k = nex[k])if (!vis[p[k]] && c[k] && dist[x] - cc[k] == dist[p[k]])
{
//这个条件就表示这条边可以进行増广
a = dfs(p[k], min(c[k], low - used));
if (a)cost += a * cc[k], c[k] -= a, c[k ^ 1] += a, used += a;
//累加答案,加流等操作都在这了
if (used == low)break;
}
return used;
}
int costflow()
{
int flow = 0;
while (spfa(s, t))
{
//判断起点终点是否连通,不连通说明满流,做完了退出
vis[t] = 1;
while (vis[t])
{
memset(vis, 0, sizeof vis);
flow += dfs(s, INF);
//一直増广直到走不到为止(这样也可以省时间哦)
}
}
return flow;//这里返回的是最大流,费用的答案在cost里
}
} G;
int main()
{
int n, m;
cin >> n >> m;//输入城市个数和道路条数
int st = 0, ed = n * 7 + 2;
G.init(7 * n + 5, st, ed);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= 7; j++)
{
int x;
cin >> x;
G.addedge(st, (i - 1) * 7 + j, x, 0);
}
for (int j = 1; j <= 7; j++)
{
int x;
cin >> x;
G.addedge((i - 1) * 7 + j, ed, x, 0);
}
int v, w;
cin >> v >> w;
for (int j = 1; j <= 6; j++)
G.addedge((i - 1) * 7 + j, (i - 1) * 7 + j + 1, v, w);
G.addedge((i - 1) * 7 + 7, (i - 1) * 7 + 1, v, w);
}
for (int i = 0; i < m; i++)
{
int x, y, z;
cin >> x >> y >> z;
for (int i = 1; i <= 7; i++)
{
G.addedge((x - 1) * 7 + i, (y - 1) * 7 + i, INF, z);
G.addedge((y - 1) * 7 + i, (x - 1) * 7 + i, INF, z);
}
}
G.costflow();
cout << G.cost;
return 0;
}
各ポイントと彼の倉庫の毎日について、ポイントを構築します。
aij - bij > 0、原点はこの点に接続され、フローは aij-bij、コストは 0 です。
aij - bij < 0、ポイントはシンク ポイントに接続、フローは bij -aij、コストは 0 です。
各日の生産ポイントに対して、与えられたグラフに従って双方向変数が設定され、流量は inf であり、コストは輸送コストです。
毎日、生産ポイントはその日の倉庫に側面を接続し、フローは v、コストは w、倉庫は翌日の到達可能な生産ポイントに接続し、容量は inf、コストは輸送費。これは定期的な問題であるため、週 7 は週 1 に接続する必要があることに注意してください。
コード 2
#include <set>
#include <map>
#include <queue>
#include <cmath>
#include <vector>
#include <cstdio>
#include <utility>
#include <cstring>
#include <iostream>
#include <algorithm>
const int maxn = 3007;
const int maxm = 300003;
const int inf = 0x3f3f3f3f;
int map[maxn][maxn];
int v[maxn], w[maxn];
int a[maxn][11], b[maxn][11];
int _flow_cur, _cost_cur;
struct Edge
{
int u, v, cap, cost, next;
Edge() {
}
Edge(int u, int v, int cap, int cost, int next)
: u(u), v(v), cap(cap), cost(cost), next(next) {
}
} edges[maxm];
int head[maxn], e_cnt;
bool visited[maxn];
int SLK[maxn], dist[maxn], _start_, _end_;
void init()
{
_flow_cur = _cost_cur = 0;
e_cnt = 0;
memset(head, -1, sizeof(head));
memset(visited, 0, sizeof(visited));
}
void add(int u, int v, int cap, int cost)
{
edges[e_cnt] = Edge(u, v, cap, cost, head[u]); head[u] = e_cnt ++;
edges[e_cnt] = Edge(v, u, 0 , -cost, head[v]); head[v] = e_cnt ++;
}
bool modlabel()
{
int delta = inf;
for(int i = 0; i <= _end_; i ++) {
if( !visited[i] && SLK[i] < delta ) {
delta = SLK[i];
SLK[i] = inf;
}
}
if(delta == inf) return false;
for(int i = 0; i <= _end_; i ++) {
if( visited[i] ) dist[i] += delta;
}
return true;
}
int aug(int u, int flow, int pre)
{
if( u == _end_ ) {
_cost_cur += flow * dist[_start_];
return flow;
}
visited[u] = true;
int flow_rest = flow;
for(int i = head[u]; ~i; i = edges[i].next) {
register int v = edges[i].v;
if( !visited[v] && edges[i].cap ) {
int temp = dist[v] + edges[i].cost - dist[u];
if( !temp ) {
int delta = aug( v, std::min(flow_rest, edges[i].cap), u );
if( delta > 0 ) {
flow_rest -= delta;
edges[i].cap -= delta;
edges[i^1].cap += delta;
}
if( flow_rest == 0 ) return flow;
} else {
if( temp < SLK[v] ) {
SLK[v] = temp;
}
}
}
}
return flow - flow_rest;
}
void cost_flow()
{
for(int i = 0; i <= _end_; i ++) {
dist[i] = 0;
SLK[i] = inf;
}
do {
int ret = 0;
do {
_flow_cur += ret;
memset(visited, 0, sizeof(visited));
ret = aug(_start_, inf, -1);
} while( ret );
} while( modlabel() );
}
int main()
{
int n, m;
while(~scanf("%d %d", &n, &m)){
init();
for(int i = 1; i <= n; i ++) {
for(int j = 0; j <= 6; j ++) {
scanf("%d", &a[i][j]);
}
for(int j = 0; j <= 6; j ++) {
scanf("%d", &b[i][j]);
}
scanf("%d %d", &v[i], &w[i]);
}
memset(map, 0, sizeof(map));
for(int i = 0 ; i < m; i ++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
map[u][v] = map[v][u] = w;
}
int _st = 0, _ed = 14*n+1;
_start_ = _st, _end_ = _ed;
for(int i = 0; i <= 6; i ++) {
for(int j = 1; j <= n; j ++) {
int id_b = i * n; // 生产点base
int id_c = (i + 7) * n; // 仓库点base
if(a[j][i] > b[j][i]) add(_st, id_b+j, a[j][i]-b[j][i], 0);
if(a[j][i] < b[j][i]) add(id_b+j, _ed, b[j][i]-a[j][i], 0);
add(id_b+j, id_c+j, v[j], w[j]);
int nxt = ((i + 1) % 7) * n; // 下一天的生产点base
add(id_c+j, nxt+j, inf, 0);
for(int k = 1; k <= n; k ++) {
if(!map[j][k]) continue;
add(id_b+j, id_b+k, inf, map[j][k]);
add(id_c+j, nxt+k, inf, map[j][k]);
}
}
}
cost_flow();
printf("%d\n", _cost_cur);
}
}
2015 年 3 月
画像の回転
少しシミュレーション
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
using namespace std;
int a[1000][1000];
int main() {
int m, n;
scanf("%d %d",&n,&m);
int i, j;
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
{
scanf("%d",&a[i][j]);
}
}
for (j = m-1; j >=0; j--)
{
for (i = 0; i <n; i++)
{
printf("%d ",a[i][j]);
}
printf("\n");
}
return 0;
}
番号順
構造体の並べ替え
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
using namespace std;
const int maxn = 1010;
struct Num
{
int num;
int count;
}a[maxn];
bool cmp(Num a, Num b)
{
if (a.count != b.count) return a.count > b.count;
else return a.num < b.num;
}
int main() {
int n;
int i;
int num;
scanf("%d",&n);
for (i = 0; i < maxn; i++)
{
a[i].num = i;
a[i].count = 0;
}
for (i = 0; i < n; i++)
{
scanf("%d",&num);
a[num].count++;
}
sort(a, a + maxn,cmp);
i = 0;
while (a[i].count != 0)
{
printf("%d %d\n", a[i].num, a[i].count);
i++;
}
return 0;
}
まつり
典型的な年と日付の問題: 共通のテンプレートである場所がいくつかあります
1. うるう年の検索
int run(int year)//闰年返回1,非闰年返回0
{
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
return 1;//是闰年
else
return 0;//不是闰年
}
2. 計算は曜日で、
1850 年 1 月 1 日が週 2 であることがわかります。
int compute(int y, int a)
{
int sum = 0;
for (int i = 1850; i <= y - 1; i++)
{
sum += dayday[run(i)];
}
//
int flag = run(y);
for (int i = 1; i <= a - 1; i++)
{
sum += mouth[flag][i];
}
sum++;//因为是星期2,所以sum++
sum %= 7;
sum++;
return sum;
}
コード:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
int year1, year2;
int a, b, c;
int mouth[2][13] = {
{
0,31,28,31,30,31,30,31,31,30,31,30,31}, {
0,31,29,31,30,31,30,31,31,30,31,30,31} };
//此函数计算y年a月是星期几
int dayday[2] = {
365,366 };
int run(int year)//闰年返回1,非闰年返回0
{
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
return 1;//是闰年
else
return 0;//不是闰年
}
int compute(int y, int a)
{
int sum = 0;
for (int i = 1850; i <= y - 1; i++)
{
sum += dayday[run(i)];
}
//
int flag = run(y);
for (int i = 1; i <= a - 1; i++)
{
sum += mouth[flag][i];
}
sum++;
sum %= 7;
sum++;
return sum;
}
int main() {
scanf("%d %d %d %d %d",&a,&b,&c,&year1,&year2);
for (int y = year1; y <= year2; y++)
{
int tempb = b;
//计算y年a月1号是星期几
int days = compute(y,a);
//printf("是星期%d\n",days);
int day = 1;//天数
int sumday = mouth[run(y)][a];//最多能数到几号
//printf("sumday=%d\n",sumday);
while (day<=sumday)
{
if (days == c)
tempb--;
if (tempb == 0)
break;
days++;
if(days>7)
days = days % 7;
day++;
}
if (day > sumday)
printf("none\n");
else
printf("%d/%02d/%02d\n",y,a,day);//注意加上02,不然只有30分
}
return 0;
}
ネットワーク遅延
方法 1 :
すべてのソースの最短パスで最大の長さのものを見つけます。
質問の入力により、すべてのポイントが互いに接続できることが保証されるため
、maxv が大きすぎることはありません。そうしないと、間違って実行されます。
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<limits>
using namespace std;
const int INF = 1000000000;
const int maxv = 210;//最大顶点数
int n, m;
int dis[maxv][maxv];
void Floyd()
{
for (int k = 1; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (dis[i][k] != INF && dis[k][j] != INF && dis[i][k] + dis[k][j] < dis[i][j])
{
dis[i][j] = dis[i][k] + dis[k][j];
}
}
}
}
}
int main()
{
int u, v, w;
fill(dis[0], dis[0] + maxv * maxv, INF);
int d1, d2;
scanf("%d %d",&d1,&d2);
n = d1 + d2;//顶点数
m = n - 1;//边数
int temp;
for (int i = 1; i <= n; i++)
{
dis[i][i] = 0;
}
for (int i = 2; i <= n; i++)
{
scanf("%d",&temp);
dis[temp][i] = 1;
dis[i][temp] = 1;
}
Floyd();
int max=0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (dis[i][j] != INF)
{
if (dis[i][j] > max)
max = dis[i][j];
}
}
}
printf("%d\n",max);
return 0;
}
方法 2 : 木の直径は、
まずこの点 BFS または DFS から最も遠い点 u までであり、次にこの u 点 BFS または DFS から別の点 v までであり、u->v の長さが木の直径です。 、これがこの質問の答えです。
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<limits>
using namespace std;
const int maxn = 20000 + 5;
vector<int> G[maxn];
bool vis[maxn] = {
};
int n, m, ans = 0, depth[maxn];
int main()
{
cin >> n >> m;
for (int i = 2, u; i <= n + m; ++i) {
cin >> u;
G[u].push_back(i);
G[i].push_back(u);
}
queue<int> Q;
Q.push(1);
vis[1] = true;
int now;
while (!Q.empty()) {
now = Q.front();
Q.pop();
for (int i = 0; i < G[now].size(); ++i) {
int v = G[now][i];
if (vis[v]) continue;
vis[v] = true;
Q.push(v);
}
}
Q.push(now);
memset(vis, false, sizeof(vis));
vis[now] = true;
while (!Q.empty()) {
now = Q.front();
Q.pop();
for (int i = 0; i < G[now].size(); ++i) {
int v = G[now][i];
if (vis[v]) continue;
vis[v] = true;
depth[v] = depth[now] + 1;
Q.push(v);
}
}
cout << depth[now];
}
最低費用
当初、この問題を和集合探索+Kアルゴリズムで解こうと思ったのですが、コストを計算する必要があり、計算方法が分からず戸惑いました
。
30 ポイント コード 1:
#define _CRT_SECURE_NO_WARNINGS 1
#include <cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cstdlib>
#include<iostream>
using namespace std;
#define N 100001
long long p[N], ans;
bool vis[N];
typedef pair<int, int> Pair;
vector<Pair> g[N];
void dfs(int s, int t, long long price)
{
long long minprice;
long long cost;
if (s == t)
{
cout << ans << endl;
}
else
{
vis[s] = true;
for (int i = 0; i < g[s].size(); i++)
{
if (!vis[g[s][i].first])
{
minprice = min(price, p[s]);
cost = g[s][i].second * minprice;
ans += cost;
dfs(g[s][i].first, t, minprice);
ans -= cost;
}
}
}
}
int main()
{
std::ios::sync_with_stdio(false);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> p[i];
}
while (--n)
{
int u, v, e;
cin >> u >> v >> e;
g[u].push_back(Pair(v, e));
g[v].push_back(Pair(u, e));
}
while (m--)
{
int s, t;
cin >> s >> t;
memset(vis, false, sizeof(vis));
ans = 0;
dfs(s, t, p[s]);
}
return 0;
}
30 ポイント コード 2:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
struct Node {
long long from, to, cost;
Node (long long f, long long t, long long c) : from(f), to(t), cost(c) {
}
};
const long long maxn = 100000 + 5;
long long n, m, cost[maxn];
vector<Node> G[maxn];
vector<long long> Path, path, Dis, dis; // 真假path
bool vis[maxn];
void DFS(long long from, long long to)
{
if (from == to) {
Path = path;
Dis = dis;
return;
}
for (long long i = 0, v, d; i < G[from].size(); ++i) {
v = G[from][i].to;
d = G[from][i].cost;
if (vis[v]) continue;
vis[v] = true;
path.push_back(v);
dis.push_back(d);
DFS(v, to);
path.pop_back();
dis.pop_back();
}
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m;
for (long long i = 1; i <= n; ++i)
cin >> cost[i];
for (long long i = 1, u, v, c; i < n; ++i) {
cin >> u >> v >> c;
G[u].push_back(Node(u, v, c));
G[v].push_back(Node(v, u, c));
}
for (long long i = 0, from, to; i < m; ++i) {
cin >> from >> to;
memset(vis, 0, sizeof(vis));
path.clear();
dis.clear();
path.push_back(from);
vis[from] = true;
DFS(from, to);
long long _min = cost[from], cos = 0, ans = 0;
for (long long i = 0; i < Path.size() - 1; ++i) {
if (cost[Path[i]] < _min) {
ans += cos*_min;
cos = 0;
_min = cost[Path[i]];
}
cos += Dis[i];
}
ans += _min*cos;
cout << ans << endl;
}
}
2015 年 9 月
シーケンスセグメンテーション
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
using namespace std;
int main() {
int n, num, i;
int duibi;
int duan;
scanf("%d",&n);
scanf("%d",&num);
duibi = num;
duan = 1;
for (i = 1; i < n; i++)
{
scanf("%d", &num);
if (num != duibi)
{
duan++;
duibi = num;
}
}
printf("%d",duan);
return 0;
}
日付計算
各月の日付を格納する 2 桁の配列を設定します
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
using namespace std;
int mouths[2][13] = {
{
0,31,28,31,30,31,30,31,31,30,31,30,31},{
0,31,29,31,30,31,30,31,31,30,31,30,31} };
int main() {
int year, days;
int mouth=0, day;
scanf("%d %d",&year,&days);
int flag;
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
flag = 1;
else
flag = 0;
while (days > 0)
{
if (days <= mouths[flag][mouth + 1])
break;
else
{
days -= mouths[flag][mouth + 1];
mouth++;
} }
printf("%d\n%d",mouth+1,days);
return 0;
}
テンプレート生成システム
より友好的なのは、この質問が入力行の数を示し、行ごとに置き換えることができることです
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<map>
using namespace std;
vector<string> str;
string tempstr;
string str1, str2;
map<string, string> mp;
int n, m;
int main() {
scanf("%d %d",&m,&n);
getchar();
for (int i = 0; i < m; i++)
{
getline(cin,tempstr);
str.push_back(tempstr);
}
for (int i = 0; i < n; i++)
{
getline(cin,tempstr);
int t1 = tempstr.find("\"");
str1 = tempstr.substr(0,t1-1);
tempstr.erase(0,t1+1);
int t2 = tempstr.find("\"");
str2 = tempstr.substr(0,t2);
mp[str1] = str2;
}
map<string, string>::iterator it;
/*for ( it = mp.begin(); it!=mp.end(); it++)
{
cout << it->first << " " << it->second << endl;
}*/
for (int i = 0; i < m; i++)
{
int prev, next;
prev = 0;
while(1)
{
if ((prev = str[i].find("{
{ ", prev)) == (int)string::npos)
break;
if ((next = str[i].find(" }}", prev)) == (int)string::npos)
break;
string key = str[i].substr(prev + 3, next - prev - 3);
str[i].replace(prev, next - prev + 3, mp.count(key) ? mp[key] : "");
prev += mp.count(key) ? mp[key].length() : 0; // 避免重复替换,因为替换的关键词里面可能有{
{
}
}
for (int i = 0; i < m; i++)
{
cout<<str[i]<<endl;
}
return 0;
}
高速道路
最初はBFSでやっていましたが、タイムアウトかメモリオーバーで60点、接続できればIns配列の値を1にします
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<map>
using namespace std;
const int maxv = 1010;//城市数量
//求连通分量的个数,使用BFS邻接表
vector<int> Adj[maxv];
int n;//n为顶点数
int m;//m是单向高速公路的数量
int inq[maxv] = {
0 };//初始所有城市都没有被访问过
int ins[maxv][maxv];
int ans=0;//连通块数量
int BFS(int s)//判断u能否到达t,如果能到达,返回1,否则返回0
{
queue<int> q;
fill(inq,inq+maxv,0);
q.push(s);
inq[s] = 1;
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = 0; i < Adj[u].size(); i++)
{
int v = Adj[u][i];
ins[s][v] = 1;//表示可以到达;
ins[u][v] = 1;
if (inq[v] == 0)
{
q.push(v);
inq[v] = 1;
}
}
}
return 0;//无法连通
}
int main() {
scanf("%d %d",&n,&m);//n个城市,m条道路
int u, v;
fill(ins[0],ins[0]+maxv*maxv,0);
for (int i = 1; i <= m; i++)
{
scanf("%d %d",&u,&v);//u->v
Adj[u].push_back(v);//u->v
}
for (int i = 1; i <= n; i++)
{
BFS(i);
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (ins[i][j] && ins[j][i] && j != i)
ans++;
}
}
printf("%d",ans/2);
return 0;
}
強く連結されたコンポーネントの問題を解決するための特別な Tarjan アルゴリズムがあります:
Tarjan アルゴリズム:
ネイキッド コード。
入力:
グラフの有向グラフ。
出力:
強連結成分のそれぞれ。
入力:
6 8
1 3
1 2
2 4
3
4 3
5
4 6 4 1
5 6
出力:
6
5
3 4 2 1
ベアコード:
#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;
struct node {
int v,next;
}edge[1001];
int DFN[1001],LOW[1001];
int stack[1001],heads[1001],visit[1001],cnt,tot,index;
void add(int x,int y)
{
edge[++cnt].next=heads[x];
edge[cnt].v = y;
heads[x]=cnt;
return ;
}
void tarjan(int x)//代表第几个点在处理。递归的是点。
{
DFN[x]=LOW[x]=++tot;// 新进点的初始化。
stack[++index]=x;//进站
visit[x]=1;//表示在栈里
for(int i=heads[x];i!=-1;i=edge[i].next)
{
if(!DFN[edge[i].v]) {
//如果没访问过
tarjan(edge[i].v);//往下进行延伸,开始递归
LOW[x]=min(LOW[x],LOW[edge[i].v]);//递归出来,比较谁是谁的儿子/父亲,就是树的对应关系,涉及到强连通分量子树最小根的事情。
}
else if(visit[edge[i].v ]){
//如果访问过,并且还在栈里。
LOW[x]=min(LOW[x],DFN[edge[i].v]);//比较谁是谁的儿子/父亲。就是链接对应关系
}
}
if(LOW[x]==DFN[x]) //发现是整个强连通分量子树里的最小根。
{
do{
printf("%d ",stack[index]);
visit[stack[index]]=0;
index--;
}while(x!=stack[index+1]);//出栈,并且输出。
printf("\n");
}
return ;
}
int main()
{
memset(heads,-1,sizeof(heads));
int n,m;
scanf("%d%d",&n,&m);
int x,y;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
}
for(int i=1;i<=n;i++)
if(!DFN[i]) tarjan(i);//当这个点没有访问过,就从此点开始。防止图没走完
return 0;
}
タージャン ボード 2 :
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<map>
using namespace std;
#define MAX 10005
stack<int> s;
vector<int> G[MAX];
int DFN[MAX];
int LOW[MAX];
int vis[MAX];
int instack[MAX];
int res = 0;
int order = 0;
void Tarjan(int u)
{
DFN[u] = LOW[u] = ++order; //为结点u设定次序号和Low值
s.push(u);//将结点u压入到堆栈当中
instack[u] = 1;
vis[u] = 1;
for (int j = 0; j < G[u].size(); j++) //枚举每一条边
{
int v = G[u][j];
if (vis[v] == 0)
{
Tarjan(v); //继续DFS
LOW[u] = min(LOW[u], LOW[v]);
}
else if (instack[v]) //如果结点v仍然在栈内
{
LOW[u] = min(LOW[u], DFN[v]);
}
}
if (DFN[u] == LOW[u])//如果结点u是强联通分量的根
{
int cnt = 0;
int m;
do {
m = s.top(); //将结点m退栈,说明此事结点m是强联通分量中的一个顶点
//printf("%d ", m);//板子注释1
instack[m] = 0;
s.pop();
cnt++;
} while (m != u);
// printf("\n");//板子注释2
if (cnt > 1)
{
res = res + cnt * (cnt - 1) / 2;
}
}
return;
}
int main()
{
int n, m;
int a, b;
scanf("%d%d", &n, &m);
memset(vis, 0, sizeof(vis));
memset(G, 0, sizeof(G));
memset(DFN, 0, sizeof(DFN));
memset(LOW, 0, sizeof(LOW));
memset(instack, 0, sizeof(instack));
for (int i = 0; i < m; i++)
{
scanf("%d %d", &a, &b);
G[a].push_back(b);
}
for (int i = 1; i <= n; i++)
{
if (vis[i] == 0)
{
vis[i] = 1;
Tarjan(i);
}
}
printf("%d\n", res);
return 0;
}
最高の記事
AC オートマトン + マトリックス クイック パワー
リファレンス リンク
質問の意味: おそらく辞書を教えてください. 辞書の文字の総数は 100 を超えません. 長さ m (<=1e15) の文字列を作成するには、最も多くの辞書内の単語。重複の最大数を出力します
アイデア: データのサイズを見て、質問を行った経験に基づいて、AC オートマトンを使用して状態を最適化し、行列高速べき乗を使用して dp を最適化することを考えるのは簡単ですが、この質問はは非常に特別です。
最初に dp を書きましょう
dp[i][j]=max(dp[i-1][j の可能な最後の状態])+End[j] i は長さ
、j は AC オートマトンのノードです
が、一般的な行列の高速べき乗は線形再帰を完了するためにのみ使用でき、通常は d[i][j]=A d[i-1][a1]+B d[i-2][a2]+ を解くために使用されます。 .. 同様の問題
この問題は、最大値を維持することです。
ここでは、行列乗算の変換について説明します。行列の乗算が実行可能である理由は、乗算の結合法則によるものですが、addition と max() にも結合法則があります. 行列の高速
累乗に関する別の古典的な質問を復習し、有向グラフには複数のエッジがある可能性があることを伝えましょう.始点と終点からちょうど k ステップで、始点から終点まで移動する方法はいくつありますか。最後に、dp は次
のように記述されます
。
、次に列 0 の数値を合計します。
シグマを最大にし、乗算記号をプラス記号に変更すると、この問題の式とまったく同じになります。
実際、このアプローチは機能します。
この問題については、行列の乗算の定義を少し変更するだけで、最大値を取る dp を最適化するために使用できます。
この種の dp は非常に一般的であり、高速な行列累乗のこの方法により、dp の最適化を加速するための新しい世界への扉が開かれます。
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <Stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout << "[" << x << "]"
#define FIN freopen("input.txt", "r", stdin)
#define FOUT freopen("output.txt", "w+", stdout)
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef vector<LL> vec;
typedef vector<vec> mat;
const int MX = 1e4 + 5;
const LL INF = 0x3f3f3f3f3f3f3f3f;
int rear, root;
int Next[MX][26], Fail[MX], End[MX];
int New() {
rear++;
End[rear] = 0;
for(int i = 0; i < 26; i++) {
Next[rear][i] = -1;
}
return rear;
}
void Init() {
rear = 0;
root = New();
}
void Add(char *A) {
int n = strlen(A), now = root;
for(int i = 0; i < n; i++) {
int id = A[i] - 'a';
if(Next[now][id] == -1) {
Next[now][id] = New();
}
now = Next[now][id];
}
End[now]++;
}
void mat_fill(mat &A, LL val) {
for(int i = 0; i < A.size(); i++) {
for(int j = 0; j < A[0].size(); j++) {
A[i][j] = val;
}
}
}
mat Build() {
queue<int>Q;
Fail[root] = root;
for(int i = 0; i < 26; i++) {
if(Next[root][i] == -1) {
Next[root][i] = root;
} else {
Fail[Next[root][i]] = root;
Q.push(Next[root][i]);
}
}
while(!Q.empty()) {
int u = Q.front(); Q.pop();
End[u] += End[Fail[u]];
for(int i = 0; i < 26; i++) {
if(Next[u][i] == -1) {
Next[u][i] = Next[Fail[u]][i];
} else {
Fail[Next[u][i]] = Next[Fail[u]][i];
Q.push(Next[u][i]);
}
}
}
mat A(rear, vec(rear));
mat_fill(A, -INF);
for(int i = 1; i <= rear; i++) {
for(int j = 0; j < 26; j++) {
int chd = Next[i][j];
A[chd - 1][i - 1] = End[chd];
}
}
return A;
}
mat mat_mul(mat &A, mat &B) {
mat C(A.size(), vec(B[0].size()));
mat_fill(C, -INF);
for(int i = 0; i < A.size(); i++) {
for(int j = 0; j < B[0].size(); j++) {
for(int k = 0; k < B.size(); k++) {
if(A[i][k] + B[k][j] >= 0) {
C[i][j] = max(C[i][j], A[i][k] + B[k][j]);
}
}
}
}
return C;
}
mat mat_pow(mat A, LL n) {
mat B = A; n--;
while(n) {
if(n & 1) B = mat_mul(B, A);
A = mat_mul(A, A);
n >>= 1;
}
return B;
}
void print(mat &A) {
for(int i = 0; i < A.size(); i++) {
for(int j = 0; j < A[0].size(); j++) {
fuck(A[i][j]);
} printf("\n");
}
}
char S[MX];
int main() {
int n; LL m; //FIN;
scanf("%d%lld", &n, &m);
Init();
for(int i = 1; i <= n; i++) {
scanf("%s", S);
Add(S);
}
mat A = Build();
A = mat_pow(A, m);
LL ans = 0;
for(int i = 0; i < rear; i++) {
ans = max(ans, A[i][0]);
}
printf("%lld\n", ans);
return 0;
}
2015-12
桁の合計
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
using namespace std;
int main() {
int a;
int sum=0;
scanf("%d",&a);
while (a != 0)
{
sum += a % 10;
a /= 10;
}
printf("%d",sum);
return 0;
}
エリミネーションゲーム
ハッシュテーブル ストレージ
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iostream>
#include<string>
#include<set>
using namespace std;
int a[30][30];
int hashtable[30][30];
int main() {
int m, n;
int i, j;
scanf("%d %d",&n,&m);
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
{
scanf("%d",&a[i][j]);
hashtable[i][j] = 0;
}
}
//先遍历每一行
for (i = 0; i < n; i++)
{
for (j = 0; j <= m - 3; j++)
{
if (a[i][j] == a[i][j + 1] && a[i][j + 1] == a[i][j + 2])
{
hashtable[i][j] = 1;
hashtable[i][j + 1] = 1;
hashtable[i][j + 2] = 1;
}
}
}
for (j = 0; j < m; j++)
{
for (i = 0; i <= n - 3; i++)
{
if (a[i][j] == a[i+1][j] && a[i+1][j] == a[i+2][j])
{
hashtable[i][j] = 1;
hashtable[i+1][j] = 1;
hashtable[i+2][j] = 1;
}
}
}
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
{
if (hashtable[i][j] == 1)
printf("0 ");
else
printf("%d ",a[i][j]);
}
printf("\n");
}
return 0;
}
描く
BFSディープサーチ+ハッシュテーブルで横線か縦線か判断
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<map>
using namespace std;
int n, q, m;
int op;//操作
const int maxn = 110;
char pic[maxn][maxn];//画布
int hashtable[maxn][maxn];//=0表示没有画线//==1表示画横线,=2表示画竖线=3表示画十字
int inq[maxn][maxn];
int X[4] = {
0,0,1,-1 };
int Y[4] = {
1,-1,0,0 };
struct node {
int x;
int y;
}Node;
int judge(int x, int y)//检测(x,y)是否合法
{
if (x >= m || x < 0 || y >= n || y < 0) return 0;//越界
if (hashtable[y][x] != 0 || inq[y][x] == 1) return 0;//已经被画线了或者已经入过队了
return 1;
}
void BFS(int x,int y,char c)
{
queue<node> Q;
Node.x = x; Node.y = y;
Q.push(Node);
inq[y][x] = 1;//记录为入队
pic[y][x] = c;
while (!Q.empty())
{
node top = Q.front();
Q.pop();
for (int i = 0; i < 4; i++)
{
int newX = top.x + X[i];
int newY = top.y + Y[i];
if (judge(newX, newY))
{
Node.x = newX;
Node.y = newY;
Q.push(Node);
inq[newY][newX] = 1;
pic[newY][newX] = c;
}
}
}
}
int main() {
scanf("%d %d %d",&m,&n,&q);//一共m列n行
getchar();
//初始化画布:
for (int y = 0; y < n; y++)
{
for (int x = 0; x < m; x++)
{
pic[y][x] = '.';
}
}
//初始化hashtable
fill(hashtable[0],hashtable[0]+maxn*maxn,0);
for (int i = 0; i < q; i++)
{
scanf("%d",&op);
if (op==1)//填充操作
{
//使用BFS进行广度优先搜索填充
//每次填充字符的时候都要初始化inq
fill(inq[0],inq[0]+maxn*maxn,0);
int x, y;
char c[3];
scanf("%d %d %s", &x, &y, c);
BFS(x,y,c[0]);
}
else//画线段操作
{
int x1, y1, x2, y2;
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
if (x1 == x2)//表示画竖线
{
if (y1 > y2)
swap(y1,y2);
for (int y = y1; y <= y2; y++)
{
if(hashtable[y][x1]==0)
hashtable[y][x1] = 2;
else
hashtable[y][x1] = 3;
}
}
else//表示画横线
{
if (x1 > x2)
swap(x1,x2);
for (int x = x1; x <= x2; x++)
{
if (hashtable[y1][x] == 0)
hashtable[y1][x] = 1;
else
hashtable[y1][x] = 3;
}
}
}
}
//现在把画线段的操作放进pic中
for (int y = 0; y < n; y++)
{
for (int x = 0; x < m; x++)
{
if (hashtable[y][x] == 1)//说明画横线
pic[y][x] = '-';
else if (hashtable[y][x] == 2)
pic[y][x] = '|';//画竖线
else if (hashtable[y][x] == 3)//画十字架
pic[y][x] = '+';
else
;
}
}
for (int y = n-1; y>=0;y--)
{
for (int x = 0; x < m; x++)
{
printf("%c", pic[y][x]);
}
printf("\n");
}
return 0;
}
商品を届ける
問題の説明: 上記を参照して、最小の辞書順で解を見つけてください。
問題分析: この問題は一筆書きの問題、またはオイラー経路の問題です。グラフが連結グラフかどうかを判断し、次にオイラー パスがあるかどうかを判断する必要があります。それが接続されたグラフであるかどうかを判断するには、ユニオン検索を使用してそれを達成できます。オイラー経路が存在するかどうかを判断する条件は、無向グラフのすべてのノードの次数が偶数であるか、次数が奇数のノードが 2 つあることです。この条件を満たすグラフは、オイラー パスを見つけることができなければなりません。ノード 1 から開始するため、出入度が奇数のノードが 2 つある場合、1 の出入度は奇数に違いありません。
プログラムの説明: プログラムでは、隣接リストを使用してグラフを表現し、セットを使用します。コレクションクラス集合は自然ソートの特徴を持っており、集合構築後の特別なソートは必要ありません。このように、DFS を使用してオイラー パスを見つける場合、最初に見つかった解は辞書式順序で最小の解です。
参照コード
/* CCF201512-4 送货 */
#include <iostream>
#include <cstring>
#include <set>
#include <vector>
#include <stack>
using namespace std;
const int N = 10000;
// 并查集类
int v[N+1];
class UF {
private:
int length;
public:
UF(int n) {
length = n;
for(int i=0; i<=n; i++)
v[i] = i;
}
// 压缩
int Find(int x) {
if(x == v[x])
return x;
else
return v[x] = Find(v[x]);
}
bool Union(int x, int y) {
x = Find(x);
y = Find(y);
if(x == y) {
return false;
} else {
v[x] = y;
return true;
}
}
};
set<int> g[N+1]; // 用邻接表存储图,用集合进行排序
stack<int> path; // 用于保存欧拉路径
bool visited[N+1][N+1];
int nopathflag;
int n, m;
// 深度优先搜索
void dfs(int node)
{
for(set<int>::iterator it=g[node].begin(); it!=g[node].end(); it++) {
if(!visited[node][*it]) {
visited[node][*it] = true;
visited[*it][node] = true;
dfs(*it);
}
}
path.push(node);
}
int main()
{
int src, dest;
// 输入数据
cin >> n >> m;
UF uf(n);
for(int i=0; i<m; i++) {
cin >> src >> dest;
g[src].insert(dest);
g[dest].insert(src);
uf.Union(src, dest);
}
// 判断图的联通性
nopathflag = false;
int root = uf.Find(1);
for(int i=2; i<=n; i++)
if(uf.Find(i) != root) {
nopathflag = true;
break;
}
// 判定是否存在欧拉路径:根据出入度进行计算
if(!nopathflag) {
// 计算出入度
int count = 0;
for(int i=1; i<=n; i++)
if(g[i].size() % 2 == 1)
count++;
if(!(count == 0 || (count == 2 && g[1].size() % 2 == 1)))
nopathflag = true;
}
if(!nopathflag) {
// 计算路径:从结点1开始深度优先搜索
memset(visited, false, sizeof(visited));
dfs(1);
// 输出结果
int t;
while(!path.empty()) {
t = path.top();
path.pop();
cout << t << ' ';
}
cout << endl;
} else
// 输出结果:未找到路径
cout << -1 << endl;
return 0;
}
マトリックス
行列の高速累乗のボード問題
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 200, MOD = 1e9 + 7;
int n;
struct mat {
int m[maxn][maxn];
}a,b,unit;
void init() {
memset(a.m, 0, sizeof a.m);
memset(b.m, 0, sizeof b.m);
memset(unit.m, 0, sizeof unit.m);
}
mat operator * (mat m1, mat m2) {
mat r;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
int l = 0;
for (int k = 0; k < n; k++) {
l = l ^ (m1.m[i][k] & m2.m[k][j]);
}
r.m[i][j] = l;
}
}
return r;
}
mat quick_pow(mat aa, int x) {
mat t = aa;
while (x) {
if (x & 1) {
t = t * aa;
}
aa = aa * aa;
x >>= 1;
}
return t;
}
int main(){
init();
for (int i = 0; i < maxn; i++) {
for (int j = 0; j < maxn; j++) {
if (j == i) unit.m[i][j] = 1;
else unit.m[i][j] = 0;
}
}
cin >> n;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++){
scanf("%1d", &a.m[i][j]);
}
}
for (int j = 0; j < n; j++) scanf("%1d", &b.m[j][0]);
int q; cin >> q;
while (q--) {
int k; cin >> k;
if (k == 0) {
for (int i = 0; i < n; i++) cout << b.m[i][0];
cout <<endl;
continue;
}
mat rr = quick_pow(a, k - 1) * b;
for (int i = 0; i < n; i++) cout << rr.m[i][0];
cout <<endl;
}
return 0;
}